Лямбда значэнне ўласцівасці селектара ў якасці параметру

У мяне ёсць патрабаванне, каб змяніць спосаб, так што ён мае дадатковы параметр, які будзе прымаць лямбда-выраз, якое будзе выкарыстоўвацца на ўнутраным аб'екце, каб вярнуць значэнне дадзенага ўласцівасці. Прабачце маё верагоднае няправільнае выкарыстанне тэрміналогіі, так як гэта мая першая вылазка ў выразы LINQ!

Я паспрабаваў знайсці адказ, але, як я ўжо казаў, мая тэрміналогія, здаецца, ад і прыкладаў, якія я магу знайсці занадта складаныя або мець справу з выразамі для функцый збору, такіх як .где() , які я знаёмы з.

Тое, што я да гэтага часу (зрэзаная версія):

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

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

Я хацеў бы назваць гэта нешта накшталт гэтага:

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

альбо:

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

Відавочна, што дэталі, якія я прапускаю гэта Селектар параметр MyMethod , як прымяніць яго да лакальнай зменнай і як перадаць патрабаванае ўласцівасць у метад, калі я выкліку гэта.

Любая дапамога будзе ацэнена, а таксама дадатковыя бонусныя балы за рашэнні VB.NET, а таксама, на жаль, канчатковае ажыццяўленне павінна быць у нашым адзінокім праекце VB!

27
Проста цікава: Какая мэта мае testParameter ? Здаецца, нічога не рабіць ,
дададзена аўтар Matt, крыніца

10 адказы

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

Пры выкарыстанні Func дэлегаты, апошні параметр з'яўляецца тып звароту і першым N-1 з'яўляюцца тыпы аргументаў. У гэтым выпадку маецца адзіны MyObject Аргумент селектар і вяртае радок .

Вы можаце выклікаць яго, як:

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

Паколькі тып якое вяртаецца MyMethod супадае з тыпам вяртання вашага селектар дэлегат, вы маглі б зрабіць гэта радавое:

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

EDIT: Я не ведаю, VB.Net, але гэта выглядае, як гэта будзе:

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

і агульная версія будзе выглядаць так:

Public Function MyMethod(Of T)(testParameter as Integer, selector Func(Of MyObject, T))
    Return selector(_myObject)
End Function
36
дададзена
Дзякуй за адказ, я ўжо рэалізаваў яго, і гэта працуе выдатна!
дададзена аўтар XN16, крыніца
@Servy Я мяркую, гэта было б змяніць, калі я спрабаваў атрымаць доступ да іншага ўласцівасці іншага тыпу?
дададзена аўтар XN16, крыніца
Гэта сапраўды так проста?! Я адчуваю сябе трохі недарэчна аб цяпер! Якое значэнне радкі у Func дэкларацыя?
дададзена аўтар XN16, крыніца
@ XN16 Ён вызначае тып якое вяртаецца значэння дэлегаты.
дададзена аўтар Servy, крыніца
@ XN16 Ну, MyMethod вяртае радок , таму, калі дэлегат не адпавядае ня кампілюецца. Для таго, каб быць у стане дазволіць адвольнаму селектар вам трэба зрабіць MyMethod агульным і выкарыстоўваць агульны аргумент як для селектара і тыпу вяртаецца значэння.
дададзена аўтар Servy, крыніца
@ XN16 - Я дадаў кароткае тлумачэнне аргументы Func тыпу.
дададзена аўтар Lee, крыніца
@ XN16 так, калі вы звяртаецеся ўласцівасці розных тыпаў вам спатрэбіцца некалькі пераазначэнне для MyMethod. Калі вы заўсёды хочаце вярнуць іх у выглядзе радка, незалежна ад іх тыпу, то вы можаце проста змяніць дэлегат х => x.NotAString.ToString ()
дададзена аўтар Mr.Mindor, крыніца
+1 біць мяне каля 10 секунд.
дададзена аўтар Mr.Mindor, крыніца
private string MyMethod(int testParameter, Func selector)
{
    return selector(_myObject);
}

Пры выкарыстанні Func дэлегаты, апошні параметр з'яўляецца тып звароту і першым N-1 з'яўляюцца тыпы аргументаў. У гэтым выпадку маецца адзіны MyObject Аргумент селектар і вяртае радок .

Вы можаце выклікаць яго, як:

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

Паколькі тып якое вяртаецца MyMethod супадае з тыпам вяртання вашага селектар дэлегат, вы маглі б зрабіць гэта радавое:

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

EDIT: Я не ведаю, VB.Net, але гэта выглядае, як гэта будзе:

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

і агульная версія будзе выглядаць так:

Public Function MyMethod(Of T)(testParameter as Integer, selector Func(Of MyObject, T))
    Return selector(_myObject)
End Function
36
дададзена
@Servy Я мяркую, гэта было б змяніць, калі я спрабаваў атрымаць доступ да іншага ўласцівасці іншага тыпу?
дададзена аўтар XN16, крыніца
Дзякуй за адказ, я ўжо рэалізаваў яго, і гэта працуе выдатна!
дададзена аўтар XN16, крыніца
Гэта сапраўды так проста?! Я адчуваю сябе трохі недарэчна аб цяпер! Якое значэнне радкі у Func дэкларацыя?
дададзена аўтар XN16, крыніца
@ XN16 Ён вызначае тып якое вяртаецца значэння дэлегаты.
дададзена аўтар Servy, крыніца
@ XN16 Ну, MyMethod вяртае радок , таму, калі дэлегат не адпавядае ня кампілюецца. Для таго, каб быць у стане дазволіць адвольнаму селектар вам трэба зрабіць MyMethod агульным і выкарыстоўваць агульны аргумент як для селектара і тыпу вяртаецца значэння.
дададзена аўтар Servy, крыніца
@ XN16 - Я дадаў кароткае тлумачэнне аргументы Func тыпу.
дададзена аўтар Lee, крыніца
@ XN16 так, калі вы звяртаецеся ўласцівасці розных тыпаў вам спатрэбіцца некалькі пераазначэнне для MyMethod. Калі вы заўсёды хочаце вярнуць іх у выглядзе радка, незалежна ад іх тыпу, то вы можаце проста змяніць дэлегат х => x.NotAString.ToString ()
дададзена аўтар Mr.Mindor, крыніца
+1 біць мяне каля 10 секунд.
дададзена аўтар Mr.Mindor, крыніца

LINQ усё пра запыт спісу. Адказы, якія я бачыў, не справа з запытамі, таму я пакажу вам падыход, які з'яўляецца вельмі гнуткім, паколькі вы можаце <�моцны> лёгка напісаць свае ўласныя функцыі Linq , што спосаб пашырыць існуючыя функцыі або напісаць свае ўласныя функцыі.

У гэтым прыкладзе я паляпшэнне Linq ў Distinct функцыі такім чынам, каб можна было пазначыць поле, якое выкарыстоўваецца для групоўкі.

<�Моцны> Выкарыстанне (прыклад): </моцны>

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

У гэтым прыкладзе запыт быць згрупаваны па CustomerID і першы элемент кожнай групы вяртаецца.

<�Моцны> Дэкларацыя MyDistinct :

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

You can see that f, the 2nd parameter, is declared as Func, so it can be used by the .GroupBy statement.


Вяртаючыся да кода ў вашым пытанні, калі вы абвясцілі

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"}
};

вы маглі б выкарыстоўваць, што з ізноў пэўнай функцыі MyDistinct наступным чынам:

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

які будзе вяртаць

Name   Code
Test1   T
Test2   Q

or you can use .MyDistinct(d => d.Name) in the query, which returns:

Name   Code
Test1   T
Test2   Q
Test5   Q

Звярніце ўвагу на тое, што з-за MyDistinct аб'яўлены з дженеріков T і V , ён распазнае і аўтаматычна выкарыстоўвае правільныя тыпы аб'ектаў і вяртае MyObject элементы.


<�Моцны> Пашыранае выкарыстанне

Звярніце ўвагу, што MyDistinct заўсёды займае першы элемент кожнай групы. Што рабіць, калі вам трэба ўмова, якое вызначае, які элемент вам трэба?

Вось як вы можаце гэта зрабіць:

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

This modification either allows you to use it exactly as before, i.e. by specifying one parameter like .MyDistinct(d => d.Name), but it also allows you to specify a having condition such as x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2")) as a second parameter like so:

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

Калі выканаць гэты запыт, вынік:

Name   Code
Test1   T
Test2   Q
null

таму што test5 ня задавальняе ўмове (ён не ўтрымлівае 1 або 2), вы атрымліваеце NULL ў 3-м шэрагу.

Note: If you want to expose just the condition, you can have it even simpler by implementing it as:

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

У гэтым выпадку запыт будзе проста выглядаць наступным чынам:

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

so you don't need to write x=>x.FirstOrDefault(... condition ...).

4
дададзена

LINQ усё пра запыт спісу. Адказы, якія я бачыў, не справа з запытамі, таму я пакажу вам падыход, які з'яўляецца вельмі гнуткім, паколькі вы можаце <�моцны> лёгка напісаць свае ўласныя функцыі Linq , што спосаб пашырыць існуючыя функцыі або напісаць свае ўласныя функцыі.

У гэтым прыкладзе я паляпшэнне Linq ў Distinct функцыі такім чынам, каб можна было пазначыць поле, якое выкарыстоўваецца для групоўкі.

<�Моцны> Выкарыстанне (прыклад): </моцны>

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

У гэтым прыкладзе запыт быць згрупаваны па CustomerID і першы элемент кожнай групы вяртаецца.

<�Моцны> Дэкларацыя MyDistinct :

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

You can see that f, the 2nd parameter, is declared as Func, so it can be used by the .GroupBy statement.


Вяртаючыся да кода ў вашым пытанні, калі вы абвясцілі

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"}
};

вы маглі б выкарыстоўваць, што з ізноў пэўнай функцыі MyDistinct наступным чынам:

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

які будзе вяртаць

Name   Code
Test1   T
Test2   Q

or you can use .MyDistinct(d => d.Name) in the query, which returns:

Name   Code
Test1   T
Test2   Q
Test5   Q

Звярніце ўвагу на тое, што з-за MyDistinct аб'яўлены з дженеріков T і V , ён распазнае і аўтаматычна выкарыстоўвае правільныя тыпы аб'ектаў і вяртае MyObject элементы.


<�Моцны> Пашыранае выкарыстанне

Звярніце ўвагу, што MyDistinct заўсёды займае першы элемент кожнай групы. Што рабіць, калі вам трэба ўмова, якое вызначае, які элемент вам трэба?

Вось як вы можаце гэта зрабіць:

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

This modification either allows you to use it exactly as before, i.e. by specifying one parameter like .MyDistinct(d => d.Name), but it also allows you to specify a having condition such as x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2")) as a second parameter like so:

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

Калі выканаць гэты запыт, вынік:

Name   Code
Test1   T
Test2   Q
null

таму што test5 ня задавальняе ўмове (ён не ўтрымлівае 1 або 2), вы атрымліваеце NULL ў 3-м шэрагу.

Note: If you want to expose just the condition, you can have it even simpler by implementing it as:

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

У гэтым выпадку запыт будзе проста выглядаць наступным чынам:

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

so you don't need to write x=>x.FirstOrDefault(... condition ...).

4
дададзена

ў C #

Тып параметру вы шукаеце Func

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

у VB вы ўсё яшчэ хочаце Func сінтаксіс трохі адрозніваецца.

Function MyMethod(ByVal testParameter As Integer, ByVal selector as Func(Of MyClass,string) as string
    return selector(_myObject)
End Function
2
дададзена

ў C #

Тып параметру вы шукаеце Func

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

у VB вы ўсё яшчэ хочаце Func сінтаксіс трохі адрозніваецца.

Function MyMethod(ByVal testParameter As Integer, ByVal selector as Func(Of MyClass,string) as string
    return selector(_myObject)
End Function
2
дададзена

Вы можаце зрабіць гэта з дапамогай дэлегаты ад вашага выбару:

delegate string SampleDelegate(MyObject obj);

private string MyMethod(int testParameter, SampleDelegate selector)
{
    return selector(_myObject);
}
0
дададзена
class MyClass
{
    private MyObject _myObject = new MyObject() { Name = "Test", Code = "T" };

    private string MyMethod(int testParameter, Func selector)
    {
        return selector(_myObject );
    }
}
0
дададзена

Вы, верагодна, шукаеце упаўнаважаны клас ( «дэлегат» у VB, «дэлегат» ў C #), або адзін з яго падтыпаў.

This page has some examples you will probably find useful, especially near the bottom of the page.

Вось прыклад VB таго, што вы хочаце зрабіць:

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
дададзена

Вы, верагодна, шукаеце упаўнаважаны клас ( «дэлегат» у VB, «дэлегат» ў C #), або адзін з яго падтыпаў.

This page has some examples you will probably find useful, especially near the bottom of the page.

Вось прыклад VB таго, што вы хочаце зрабіць:

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
дададзена