web-dev-qa-db-fra.com

Sélecteur de valeur de propriété Lambda en tant que paramètre

Je suis obligé de modifier une méthode afin qu’elle ait un paramètre supplémentaire qui prendra une expression lambda qui sera utilisée sur un objet interne pour renvoyer la valeur de la propriété donnée. Pardonnez mon probable utilisation incorrecte de la terminologie car c'est ma première incursion dans les expressions LINQ!

J'ai essayé de chercher une réponse, mais comme je l'ai mentionné, ma terminologie semble être erronée et les exemples que je peux trouver sont beaucoup trop complexes ou traitent d'expressions pour des fonctions de collection telles que .Where(), que je connais bien.

Ce que j'ai jusqu'à présent (version réduite):

class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };

    private string MyMethod(int testParameter, ??? selector)
    {
        //return _myObject.Name;
        //return _myObject.Code;
        return ???;
    }
}

Je voudrais l'appeler quelque chose comme ça:

string result = _myClassInstance.MyMethod(1, (x => x.Name));

ou:

string result = _myClassInstance.MyMethod(1, (x => x.Code));

Évidemment, les parties qui me manquent sont le paramètre selector dans MyMethod, comment l’appliquer à la variable locale et comment transmettre la propriété requise à la méthode lorsque je l’appelle.

Toute aide serait appréciée, ainsi que des points bonus supplémentaires pour les solutions VB.NET et, malheureusement, la mise en œuvre finale doit faire partie de notre seul projet VB!

26
XN16
private string MyMethod(int testParameter, Func<MyObject, string> selector)
{
    return selector(_myObject);
}

Lorsque vous utilisez des délégués Func, le dernier paramètre est le type de retour et le premier N-1 correspond aux types d'argument. Dans ce cas, il y a un seul argument MyObject à selector et il retourne un string.

Vous pouvez l'invoquer comme ceci:

string name = _myClassInstance.MyMethod(1, x => x.Name);
string result = _myClassInstance.MyMethod(1, x => x.Code);

Comme le type de retour MyMethod correspond au type de retour de votre délégué selector, vous pouvez le rendre générique:

private T MyMethod<T>(int testParameter, Func<MyObject, T> selector)
{
    MyObject obj = //
    return selector(obj);
}

EDIT: Je ne connais pas VB.Net mais on dirait que ce serait:

Public Function MyMethod(testParameter as Integer, selector as Func(Of MyObject, String))
    Return selector(_myObject)
End Function

et la version générique serait:

Public Function MyMethod(Of T)(testParameter as Integer, selector Func(Of MyObject, T))
    Return selector(_myObject)
End Function
35
Lee

LINQ consiste à interroger des listes. Les réponses que j'ai vues ne concernent pas les requêtes. Je vais donc vous montrer une approche très flexible car vous pouvez écrire facilement vos propres fonctions Linq de cette manière pour étendre les fonctions existantes ou écrire vos propres fonctions.

Dans cet exemple, j'améliore la fonction Distinct de Linq d'une manière qui vous permet de spécifier un champ utilisé pour le regroupement.

Utilisation (exemple):

var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID);

Dans cet exemple, la requête est groupée par CustomerID et le premier élément de chaque groupe est renvoyé.

Déclaration de MyDistinct:

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                                                    Func<T, V> f)
    {
        return query.GroupBy(f).Select(x=>x.First());
    }
}

Vous pouvez constater que f, le deuxième paramètre, est déclaré en tant que Func<T, V> et peut donc être utilisé par l'instruction .GroupBy.


Revenons au code dans votre question, si vous avez déclaré

class MyObject
{
    public string Name;
    public string Code;
}

private MyObject[] _myObject = {
    new MyObject() { Name = "Test1", Code = "T"},
    new MyObject() { Name = "Test2", Code = "Q"},
    new MyObject() { Name = "Test2", Code = "T"},
    new MyObject() { Name = "Test5", Code = "Q"}
};

vous pouvez l'utiliser avec la fonction nouvellement définie MyDistinct comme suit:

var myQuery = (from x in _myObject select x).MyDistinct(d => d.Code);

qui reviendra

Nom Code
Test1 T
Test2 Q 

ou vous pouvez utiliser .MyDistinct(d => d.Name) dans la requête, qui renvoie:

Nom Code
Test1 T
Test2 Q
Test5 Q 

Notez que, puisque MyDistinct est déclaré avec les génériques T et V, il reconnaît et utilise automatiquement les bons types d'objet et renvoie les éléments MyObject.


Utilisation avancée

Notez que MyDistinct prend toujours le premier élément de chaque groupe. Que faire si vous avez besoin d'une condition définissant de quel élément vous avez besoin?

Voici comment vous pouvez le faire:

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
                                                    Func<T, V> f, 
                                                    Func<IGrouping<V,T>,T> h=null)
    {
        if (h==null) h=(x => x.First());
        return query.GroupBy(f).Select(h);
    }
}

Cette modification vous permet également de l’utiliser exactement comme avant, c’est-à-dire en spécifiant un paramètre tel que .MyDistinct(d => d.Name), mais elle vous permet également de spécifier une condition préalable telle que x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2")) en tant que second paramètre, comme suit:

var myQuery2 = (from x in _myObject select x).MyDistinct(d => d.Name,
        x=>x.FirstOrDefault(y=>y.Name.Contains("1")||y.Name.Contains("2"))
        );

Si vous exécutez cette requête, le résultat est le suivant:

Nom Code
Test1 T
Test2 Q
nul

parce que Test5 ne remplit pas la condition (il ne contient pas 1 ou 2), vous obtenez null dans la 3ème ligne.

Remarque: Si vous souhaitez exposer uniquement la condition, vous pouvez l'avoir encore plus simple en l'implémentant comme suit:

public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query,
                                                Func<T, V> f,
                                                Func<T,bool> h=null
                                                )
{
    if (h == null) h = (y => true);
    return query.GroupBy(f).Select(x=>x.FirstOrDefault(h));
}

Dans ce cas, la requête ressemblerait à ceci:

var myQuery3 = (from x in _myObject select x).MyDistinct2(d => d.Name,
                    y => y.Name.Contains("1") || y.Name.Contains("2")
                    );

vous n'avez donc pas besoin d'écrire x=>x.FirstOrDefault(... condition ...).

3
Matt

en C #

Le type de paramètre que vous recherchez Func

private string MyMethod(int testParameter, Func<MyClass,string> selector){
    return selector(_myObject);
}

dans VB vous voulez toujours Func, la syntaxe est un peu différente.

Function MyMethod(ByVal testParameter As Integer, ByVal selector as Func(Of MyClass,string) as string
    return selector(_myObject)
End Function
2
Mr.Mindor
class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };

    private string MyMethod(int testParameter, Func<MyObject, string> selector)
    {
        return selector(_myObject );
    }
}
0
Reza ArabQaeni

Vous pouvez le faire avec un délégué de votre sélecteur:

delegate string SampleDelegate(MyObject obj);

private string MyMethod(int testParameter, SampleDelegate selector)
{
    return selector(_myObject);
}
0
Teejay

Vous recherchez probablement la classe Delegate ("Delegate" en VB, "delegate" en C #) ou l'un de ses sous-types.

Cette page a quelques exemples que vous trouverez probablement utiles, en particulier au bas de la page.

Voici un exemple VB de ce que vous voudriez faire:

Public Class MyClass

  Private Property _myObject As MyObject = New MyObject With {.Name = "Test", .Code = "T"}

  Private Function MyMethod(testParameter As Integer, selector As Func(Of MyObject, String)) As String
    Return selector(_myObject).ToString
  End Function

End Class
0
Douglas Barbin