views:

166

answers:

2

I was asked to explain the ugly thing and advantages of anonymous method.

I explained possibly

Ugly thing

anonymous methods turning quickly into spaghetti code.

Advantages

We can produce thread safe code using anonymous method :Example

static List<string> Names = new List<string>(
  new string[] {
    "Jon Skeet",
    "Marc Gravell",
    "David",
    "Bill  Gates"
  });

static List<string> FindNamesStartingWith(string startingText)
{
  return Names.FindAll(
    delegate(string name)
    {
      return name.StartsWith(startingText);
    });
}

But really i did not know whether it is thread safe or not.I was asked to justify it. Can any one help me to understand (1) advantages of anonymous methods (2) Is the above code thread safe or not?

+6  A: 

Well, "thread safe" is a pretty broad term. What sort of multi-threaded use are you thinking of? I'd expect it to be safe without any data corruption or exceptions if nothing's writing to the list...

Now, as for the "ugliness" of anonymous methods... are you using C# 3? If so, start using lambda expressions, which are generally cleaner:

static List<string> FindNamesStartingWith(string startingText)
{
    return Names.FindAll(name => name.StartsWith(startingText));
}

Alternatively, using LINQ:

static List<string> FindNamesStartingWith(string startingText)
{
    return Names.Where(name => name.StartsWith(startingText)).ToList();
}

Or if you don't necessarily need a list:

static IEnumerable<string> FindNamesStartingWith(string startingText)
{
    return Names.Where(name => name.StartsWith(startingText));
}

Or if you prefer a query expression:

static IEnumerable<string> FindNamesStartingWith(string startingText)
{
    return from name in names
           where name.StartsWith(startingText)
           select name;
}

None of these look like spaghetti code to me. However, if you're going to ask whether you should use this or something else, you should really put forward an alternative. Here's a simple one:

static List<string> FindNamesStartingWith(string startingText)
{
    List<string> ret = new List<string>();
    foreach (string name in Names)
    {
        if (name.StartsWith(startingText))
        {
            ret.Add(name);
        }
    }
    return ret;
}

Do you find that clearer? If so, that's fine - but I suspect it's just that you're not really familiar with anonymous functions, LINQ etc. As you can see, there's significantly more code - it would definitely take me longer to check that that does the right thing than any of the earlier samples.

Jon Skeet
Yes I am using C# 3.0.Just they asked me to give at least one drawback and list of advantages.I did not have any point to throw to tell them the drawback so i told them may be in C# 2.0 that syntax would be spaghetti.Your examples are very useful.
I believe all those optional methods use the collection iterator, which (since the collection isn't read only) would throw if another thread came by and removed or added a name while you're doing your work. And since the collection isn't `AsReadOnly`, one can't claim its thread safe at all.
Will
@Will: You can claim it's thread-safe *in a context where nothing is modifying the list*. That's pretty much true of the whole `List<T>` type.
Jon Skeet
@Jon I was careful to say *one cannot claim* and not *you*. If they ask him "is this thread safe?" the answer can only be "no," not "well, as long as everybody agrees not to modify the collection, I kinda guess."
Will
Jon, when i write var query = Enumerable.Range(1, 100).Where(delegate(int x){return x % 2 == 0; }); can i call it deferred execution? because i did not convert it to List.
@Will thank you for your explanation Mr Will.
@Will: I don't think "no" is nearly as helpful an answer as explaining situations where it's safe and where it's not, personally. It's reasonably common to have a collection which is never modified after creation. An answer of just "no it's not thread safe at all" would encourage completely unnecessary locking in such a situation. Heck, by saying it's not thread safe at all, you could be suggesting that even locking wouldn't be enough, and the list couldn't be accessed on any thread other than the one it's created on. Unhelpful.
Jon Skeet
@nettguy: Yes, that's certainly using deferred execution. It's also streaming the results. See my latest blog post for some thoughts about the terminology in this area: http://msmvps.com/blogs/jon_skeet/archive/2010/03/25/just-how-lazy-are-you.aspx
Jon Skeet
@Jon Not to belabor the point, but this is the kind of situation that results in bugs cropping up two years down the road, after nettguy has moved on to a bigger, better job. Better to AsReadOnly it and document why than to assume (i.e., hope) nobody messes with the collection. Also, not sure how "this is not thread safe" equals "even locking won't make this thread safe." That little bit of hyperbole was unhelpful, imho. Remember, if you get hot under the collar, just count to ten and think of ponies.
Will
@Will: And your "I kinda guess" wasn't hyperbole at all? My point about even locking not being sufficient was that if you're *just* going to say that something isn't thread-safe, someone would have to assume the absolute worst - that it's thread-hostile, like Windows Forms controls. That's what happens when you just say "no" instead of giving information about the extent to which something is or isn't safe. That's why MSDN (for example) *does* give more information: "A `List<T>` can support multiple readers concurrently, as long as the collection is not modified." (cont)
Jon Skeet
Do you think MSDN should just say "`List<T>` is not thread safe" instead? Using `AsReadOnly` isn't fully safe either of course - that creates a read-only wrapper around the list, but the original list is still mutable... so you'd still have to guard against that. The best approach will depend on the situation - and we don't know enough about that to really say what's best. Maybe using `AsReadOnly` is the best approach here - or maybe it would be fine just to document that the list shouldn't be modified, if it's never exposed beyond the class.
Jon Skeet
+2  A: 

Well, it all depends on what you mean by thread-safe.

If, during the execution of FindAll, some other thread changes the underlying Names collection, then you might get odd results.

Is multiple calls to FindAll on multiple threads safe by themselves, that is, with only them executing? Yes, that's a definite yes.

As for the advantages of anonymous methods, consider the alternative.

You want to:

  1. Find some item of the collection
  2. The only way to know which item you want to find is to evaluate some expression for each one

You could do it like this:

List<string> result = new List<string>();
foreach (string name in Names)
    if (name.StartsWith(startingText))
        result.Add(name);
return result;

or, given the new lambda syntax, you could do it like this:

return Names.FindAll(name => name.StartsWith(startingText));

I know which one I prefer, but they do different things, with the same result (in this case.)

In the first case, you execute all the code yourself, there's no magic.

In the second case, you give to FindAll a way to figure out which items to put into the result. There is no non-executable way to do that in this case, you need to have FindAll execute some code, that you specify, for each item.

Lasse V. Karlsen
Thanks Lasse for sparing your time.