【问题标题】:How to access Properties of a class from a Generic Method - C#如何从泛型方法访问类的属性 - C#
【发布时间】:2017-07-03 01:36:42
【问题描述】:

我有一个具有以下属性的三类

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

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

我创建了一个接受所有这些类的通用方法。

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 属性?谁能帮忙解决这个问题

【问题讨论】:

  • 阅读有关反射的文档
  • 您应该创建一个具有这些属性或接口的抽象类,并强制 T 为接口类型或您的抽象类

标签: c# .net oop generics


【解决方案1】:

另一种可能性是使用System.Reflection

  1. 从给定类型 T 中获取带有属性名称的 PropertyInfo

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

免责声明:使用代码段落测量时间仅一次。不要按原样运行程序,而是单独运行每个单独的测试。

【讨论】:

  • 为了加快速度,请将 typeof(T).GetProperty("CustID") 放在 for 循环之外
  • @Gusman ..如果它在for循环之外,那么它将如何在forach循环中工作..值将始终相同..对吗??
  • @vmb 不,它会从类型中获取一次信息,然后在列表中的每个单独项目上使用它。结果是一样的
  • @vmb 假设具有该值的操作需要 10 毫秒并且一个包含 10000 个项目的列表,接口方法要快 7 秒。至少在我的机器上
  • @Alix 抱歉,但我无法重现此内容。我在 VS2017 中对其进行了测试,效果很好。没有错误 PropertyInfo.GetValue(Object object) 按预期工作。
【解决方案2】:

介绍界面:

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

【讨论】:

  • 或基类。 +1,想写同样的解决方案。
  • @ChristianGollhardt 这取决于。我认为,这里的接口更适合 A 类和 B 类是如此简单。
  • 你的速度很快。但是为什么现在这个方法还是通用的呢?您可以将T 完全替换为ICust
  • @vmb 什么?序列化和接口......根本没有连接
【解决方案3】:

如果你不能在现有类上引入接口或基类,另一种方法是使用动态:

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

【讨论】:

  • ..对这个有任何性能影响吗??..因为我们使用的是动态关键字??
  • @vmb :确实是的,但考虑到“如果你不能引入接口或基类”的情况,它只是比反射稍微多一点
【解决方案4】:

继承会起作用

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

【讨论】:

    【解决方案5】:

    动态

    应该解决这个问题,不要投掷或绕圈子..

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-27
      • 1970-01-01
      • 2011-08-11
      相关资源
      最近更新 更多