7

I can't work out how to do a "find" on a List I have based on use of a value that I'll pass in at run time. If you see my below code, I want to be able to find the CustomClass in the List for which it's Path parameter is equal to X, where X will be defined at run time.

Any ideas how to do such a find on a List? Or is this not possible without writing an iterator and doing the find manually? In which case perhaps there is a key'ed collection I should look at using instead?

   private List<CustomClass> files;

   public void someMethod()
  {
       Uri u= new Uri(www.test.com);
       CustomClass cc = this.files.find( matchesUri(u) );  // WON'T LET ME DO THIS
  }

   private static bool matchesUri(List<CustomClass> cc, Uri _u)
    {
        return cc.Path == _u;           }


public class CustomClass
{
    private Uri path;

    public Uri Path
    {
        get { return this.path; }
        set { this.path = value; }
    }
}

PS. I must admit I don't quite follow the predicate stuff in the doco at http://msdn.microsoft.com/en-us/library/x0b5b5bc.aspx

Greg
  • 34,042
  • 79
  • 253
  • 454

8 Answers8

12

Use a lambda:

 Uri u = new Uri("www.test.com");
 CustomClass cc = this.files.Find(cc => cc.Path == u);

or if you still want a named method:

static bool matchesUri(CustomClass cc, Uri _u)
{
    return cc.Path == _u;
}

 Uri u = new Uri("www.test.com");
 CustomClass cc = this.files.Find(cc => matchesUri(cc, u));
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
  • thanks guys - by the way - should I have been able to (if I was more up on C#) worked this out by just looking at the syntax for the method in the doco? (ie. Parameters, match, Type: System..::.Predicate<(Of <(T>)>), The Predicate<(Of <(T>)>) delegate that defines the conditions of the element to search for... – Greg Oct 07 '09 at 07:00
  • Parameter type restricts the type of delegate you may pass to the method, and thus the signature of the method or lambda you have to use to create that delegate. It does not, in and of itself, signify the use of lambdas anymore than any other delegate type does. Documentation for lambdas is here: http://msdn.microsoft.com/en-us/library/bb397687.aspx – Pavel Minaev Oct 07 '09 at 07:01
  • 1
    Delegate is similar to a function pointer. By looking at the signature (`delegate bool Predicate(T obj)`) you can see that a predicate is a function (method) that takes a **single parameter of type T**, and returns a **bool**. – vgru Oct 07 '09 at 07:33
1

You can write

CustomClass cc = this.files.Find( p=> p.Path == u );

The Find() method returns null if no element was found that matches the predicate.

Jehof
  • 34,674
  • 10
  • 123
  • 155
1

For completeness sake only, here is what you would do if you didn't want to use a lambda:

// Predicate must be a method with a single parameter,
// so we must pass the other parameter in constructor

public class UriMatcher
{
    private readonly Uri _u;
    public UriMatcher(Uri u)
    {
        _u = u;
    }

    // Match is Predicate<CustomClass>
    public bool Match(CustomClass cc)
    {
        return cc.Path == _u;
    }
}

And then use it as:

public void someMethod()
{
    Uri u = new Uri("www.test.com");
    UriMatcher matcher = new UriMatcher(u);
    CustomClass cc = this.files.Find(matcher.Match);
}

Note that you are passing a reference to a method, not the result of the method -- Match vs Match().

Check this thread also: Predicate Delegates in C#.

Community
  • 1
  • 1
vgru
  • 49,838
  • 16
  • 120
  • 201
0

In Pavel's post marked as answer, I think the line:

CustomClass cc = this.files.Find(cc => cc.Path == u);

should be instead like:

CustomClass cc = this.files.Find(cc2 => cc2.Path == u);

This is because the expresion to the left of => is a variable definition (the type is inferred from the expression) - The compiler would gives a redefinition error otherwise.

This expression can also be written with an explicit definition as:

CustomClass cc = this.files.Find((CustomClass  cc2) => cc2.Path == u);
pasx
  • 2,718
  • 1
  • 34
  • 26
0
public void someMethod()
{
    Uri u= new Uri("www.test.com");
    CustomClass cc = this.files.find( p => { return p.Path == u; } );
}
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
0

Try using anonymous method for search and use any local variable you wish inside of it. If that is not satisfactory, call out your normally defined delegate method.

Daniel Mošmondor
  • 19,718
  • 12
  • 58
  • 99
0

.NET 2.0 answer using an anonymous delegate (note that this only works for C#, VB.NET does not have anonymous delegates).

public void someMethod()
{
  Uri u= new Uri("www.test.com");
  CustomClass cc = this.files.find(delegate(CustomClass oTemp) { return oTemp.Path == u;});
}
stevehipwell
  • 56,138
  • 6
  • 44
  • 61
0

Here is a solution that I used. I needed to pass several arguments and didn't want to use anything that would prevent me from editing the method during runtime, so I came up with this.

Obviously if you wanted to you could change it to a generic method (right term?) using type arguments. This also gets around the problem of lambdas in a method. Not sure if that applies to anonymous methods as well or not, but it is already separate so no big deal.

I don't know whether the reflection would have a performance hit or not.

private Predicate<ItemData> FindItemData(string search, string fieldName)
{
    var field = typeof(ItemData).GetField(fieldName);
    return delegate(ItemData item) { return (string)field.GetValue(item) == search; };
}

//in another method...
itemlist.Find(FindItemData(e.Row[2].ToString(), "ItemName"));
Arlen Beiler
  • 15,336
  • 34
  • 92
  • 135