web-dev-qa-db-ja.com

パラメーターとしてのラムダプロパティ値セレクター

メソッドを変更して、指定されたプロパティの値を返すために内部オブジェクトで使用されるラムダ式を取得する追加のパラメーターを持つようにする必要があります。これはLINQ式への私の最初の進出なので、用語の誤った使用の可能性を許してください!

回答を検索してみましたが、前述のように、用語が間違っているようで、見つけることができる例が複雑すぎたり、慣れている.Where()などのコレクション関数の式を扱ったりしています。と。

私がこれまでに持っているもの(カットダウンバージョン):

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));

明らかに、欠けている部分はselectorMyMethodパラメータです。ローカル変数に適用する方法と、必要なプロパティを呼び出すときにメソッドに渡す方法です。

VB.NETソリューションの追加のボーナスポイント、そして残念ながら最終的な実装は、私たちの唯一のVBプロジェクトに含まれている必要があります。

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

Funcデリゲートを使用する場合、最後のパラメーターは戻り値の型であり、最初のN-1は引数の型です。この場合、MyObjectへの単一のselector引数があり、stringを返します。

あなたはそれを次のように呼び出すことができます:

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

MyMethodの戻り値の型はselectorデリゲートの戻り値の型と一致するため、ジェネリックにすることができます。

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

編集: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
35
Lee

非常に柔軟な別のアプローチを紹介します(下部のDotNetFiddleを参照):あなたは簡単に独自の [〜#〜] linq [〜#〜] functionsを使用して、既存の関数を拡張するか、独自の関数を記述して、LINQクエリの機能を活用します。

この例では、グループ化に使用するフィールドを指定できるように、LinqのDistinct関数を改善しています。

使用方法(例):

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

この例では、クエリはCustomerIDでグループ化されており、各グループの最初の要素が返されます。

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());
    }
}
_

2番目のパラメーターであるfは_Func<T, V>_として宣言されているので、_.GroupBy_ステートメントで使用できます。


宣言している場合は、質問のコードに戻ります

_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);
_

戻ります

名前コード
Test1 T
テスト2 Q

または、クエリで.MyDistinct(d => d.Name)を使用できます。

名前コード
Test1 T
テスト2 Q
テスト5 Q

MyDistinctはジェネリックTおよびVで宣言されているため、正しいオブジェクトタイプを自動的に認識して使用し、MyObject要素を返します。


高度な使用法

MyDistinctは常に各グループの最初の要素を取ることに注意してください。必要な要素を定義する条件が必要な場合はどうなりますか?

方法は次のとおりです。

_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);
    }
}
_

この変更により、以前と同じように使用できます。つまり、.MyDistinct(d => d.Name)のような1つのパラメーターを指定しますが、x => x.FirstOrDefault(y => y.Name.Contains("1")||y.Name.Contains("2"))などの条件を2番目のパラメーターとして指定することもできます。そう:

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

このクエリを実行すると、結果は次のようになります。

名前コード
Test1 T
テスト2 Q
ヌル

_Test5_は条件を満たさない(1または2を含まない)ため、3行目にnullが返されます。

注:条件のみを公開したい場合は、次のように実装することでさらに簡単にすることができます。

_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));
}
_

この場合、クエリは次のようになります。

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

したがって、x=>x.FirstOrDefault(... condition ...)を記述する必要はありません。

DotNetFiddleでお試しください

3
Matt

c#で

Funcが探しているパラメータタイプ

private string MyMethod(int testParameter, Func<MyClass,string> 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
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

セレクターのデリゲートでそれを行うことができます:

delegate string SampleDelegate(MyObject obj);

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

Delegateクラス(VBでは「Delegate」、C#では「delegate」)、またはそのサブタイプの1つを探している可能性があります。

このページ には、特にページの下部付近に役立つと思われる例がいくつかあります。

ここに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
Douglas Barbin