web-dev-qa-db-ja.com

ジェネリックメソッドからクラスのプロパティにアクセスする方法-C#

私は次のプロパティを持つ3つのクラスがあります

Class A
{
    public int CustID { get; set; }
    public string Name{ get; set; }
}

Class B
{
    public int CustID { get; set; }
    public string Age { get; set; }
}

これらのクラスをすべて受け入れるジェネリックメソッドを1つ作成しました。

public void ProceesData<T>(IList<T> param1, string date1)
{
    Parallel.ForEach(T, (currentItem) =>
    {
       // I want to aceess CustID property of param1 and pass that value to another function
        GetDetails(CustID );
        RaiseRequest<T>(param1);
    });
}

CustIDプロパティは両方のクラス(つまり、クラスAとクラスB)に存在します。このジェネリックメソッドでCustIDプロパティにアクセスするにはどうすればよいですか?誰もがこれを助けることができますか

12
vmb

別の可能性は、System.Reflection

  1. プロパティの名前を使用して、指定されたタイプPropertyInfoからTを取得します

  2. そのPropertyInfoを使用すると、GetValueを使用して、そのプロパティの対応する値を取得できます。

これを例示するための小さなテストプログラムを次に示します。

public class ClassA
{
      public int CustID { get; set; }
      public string Name { get; set; }
}

public class ClassB
{
      public int CustID { get; set; }
     public string Age { get; set; }
}
public static void ProceesData<T>(IList<T> param1, string date1)
{
    Parallel.ForEach(param1, (currentItem) =>
    {
        // I want to aceess CustID property of param1 and pass that value to another function
        var value = typeof(T).GetProperty("CustID").GetValue(currentItem);
        Console.WriteLine("Value: " + value);
    });
}
public static void Main(string[] args)
{
    List<ClassA> test = new List<ClassA>();

    test.Add(new ClassA { CustID = 123 });
    test.Add(new ClassA { CustID = 223 });
    test.Add(new ClassA { CustID = 323 });

    ProceesData<ClassA>(test, "test");
}

[〜#〜]編集[〜#〜]

これをもう少し普遍的にするには、パラメーター名をメソッドに渡すだけです。

public static void ProceesData<T>(IList<T> param1, string date1, string parameter)
{
    Parallel.ForEach(param1, (currentItem) =>
    {
        // I want to aceess CustID property of param1 and pass that value to another function
        var value = typeof(T).GetProperty(parameter).GetValue(currentItem);
        Console.WriteLine("Value: " + value);
    });
}

これで、使用するパラメーターを決定できます。

 ProceesData<ClassA>(test, "test", "Name");

または

 ProceesData<ClassB>(test, "test", "Age");

Gusmanによって提案されているように、ループの前に一度だけPropertyInfoを取得することで、少し高速化できます。

PropertyInfo pi = typeof(T).GetProperty(parameter);
Parallel.ForEach(param1, (currentItem) =>
{
    // I want to aceess CustID property of param1 and pass that value to another function
    var value = pi.GetValue(currentItem);
    Console.WriteLine("Value: " + value);
});

[〜#〜]編集[〜#〜]

どうやらパフォーマンスはあなたにとって問題のようです。これが比較です。少し時間があれば、自分で試すことができます。プロパティのアクセス時間を測定する場合:

public static void ProceesDataD<T>(IList<T> param1, string date1)
{
    Parallel.ForEach(param1, (currentItem) =>
    {
        dynamic obj = currentItem;
        int custId = obj.CustID;
    });
}
public static void ProceesData<T>(IList<T> param1, string date1) where T : ICust
{
    Parallel.ForEach(param1, (currentItem) =>
    {
        var value = currentItem.CustID;
    });
}
public static void ProceesData<T>(IList<T> param1, string date1, string parameter)
{

    PropertyInfo pi = typeof(T).GetProperty(parameter);
    Parallel.ForEach(param1, (currentItem) =>
    {
        var value = pi.GetValue(currentItem);
    });
}
public static void Main(string[] args)
{
    List<ClassA> test = new List<ClassA>();
    List<A> testA = new List<A>();

    Stopwatch st = new Stopwatch();

    for (int i = 0; i < 10000; i++)
    {
        test.Add(new ClassA { CustID = 123, Name = "Me" });
        testA.Add(new A { CustID = 123, Name = "Me" });
    }       

    st.Start();
    ProceesData<ClassA>(test, "test", "CustID");
    st.Stop();
    Console.WriteLine("Reflection: " + st.ElapsedMilliseconds);

    st.Restart();
    ProceesData<A>(testA, "test");
    st.Stop();
    Console.WriteLine("Interface: " + st.ElapsedMilliseconds);

    st.Restart();
    ProceesDataD<ClassA>(test, "test");
    st.Stop();
    Console.WriteLine("Dynamic: " + st.ElapsedMilliseconds);
}

免責事項:コードパッセージを使用して、一度に1つだけ時間を測定します。プログラムをそのまま実行するのではなく、テストを1つずつ実行してください。

9
Mong Zhu

インターフェースの紹介:

 interface ICust
 {
     public int CustID { get;}
 }
 class A : ICust
 {
     public int CustID { get; set; }
     public string Name{ get; set; }
 }

 class B : ICust
 {
     public int CustID { get; set; }
     public string Age { get; set; }
 }

 public void ProceesData<T>(IList<T> param1, string date1) where T : ICust
 {
     Parallel.ForEach(param1, (currentItem) =>
     {
         GetDetails(currentItem.CustID)
     });
 }
15
Backs

既存のクラスにインターフェースまたは基本クラスを導入できない場合、別のアプローチは動的を使用することです:

public void ProceesData<T>(IList<T> param1, string date1)
{
    Parallel.ForEach(param1, (currentItem) =>
    {
          dynamic obj = currentItem; 
          int custId = obj.CustID ;
    });
}
4
Perfect28

継承は機能します

public abstract class ABBase
{
    public int CustID { gete; set; }
}

public class A : ABBase
{
    public string Name { get; set; }
}

public class B : ABBase
{
    public string Age { get; set; }
}

次に、ジェネリックメソッドの使用ではなく

public void ProcessData(IList<ABBase> param1, string date)
{
    Parallel.ForEach(T, (currentItem) =>
    {
        GetDetails(CustID )
    });
}
1
Barry O'Kane

動的

この問題を解決する必要があります。キャストしたり、サークル内を移動したりしないでください。

    List<T> products = new List<T>();
    foreach (dynamic prod in products)
    { 
        prod.ShopCategoryID = 1 ;  // give you access to props
    }
0
Mahmoud Sayed