22

I have a three class which is having following properties

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

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

I created one generic method which accepts all these classes.

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 property is present in Both classes(ie in Class A& Class B).How can i access CustID property in this generic method ? Can anyone help on this

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
vmb
  • 2,878
  • 15
  • 60
  • 90

5 Answers5

29

Another possibility would be to use System.Reflection.

  1. Get the PropertyInfo from the given type T with the name of the property

  2. with that PropertyInfo you can use GetValue to get the corresponding value of that property.

Here is a small test programm to exemplify this:

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

EDIT

To make it a little more universal you could just pass the parameter name into the method:

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

Now you can decide what parameter you want to use:

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

or

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

As suggested by Gusman you could speed up a little by getting the PropertyInfo just once before the loop:

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

EDIT

Apparently performance seems to be an issue for you. So here is a comparison. You can try it on your own if you have a minute to wait. If we measure on the access time of the property:

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

Disclaimer: use the code passages to measure the time only one at the time. Do not run the program as it is but each single test on it's own.

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
  • 1
    To speed up things put the `typeof(T).GetProperty("CustID")` outside the for loop – Gusman Feb 14 '17 at 16:54
  • @Gusman .. if it is outside for loop,then how it will work in forach loop..value will always be same..right ?? – vmb Feb 14 '17 at 17:06
  • @vmb no it will get the Info once from the type and then use it on every individual item in the list. The result will be the same – Mong Zhu Feb 14 '17 at 17:07
  • @Mong Zhu ..Thanks...my doubt is whether reflection will slow down entie process? – vmb Feb 14 '17 at 17:18
  • @vmb how big is the list? how much time will the procedure take that you want to do with the value? All that stuff can be tested. But yes, it probably will slow down the process. It depends on the magnitude. How much can you afford? – Mong Zhu Feb 14 '17 at 17:21
  • @Mong Zhu ..list may expand even upto 10000 items..then how it will behave ?? – vmb Feb 14 '17 at 17:23
  • 1
    @vmb assuming the the operation with the value takes 10 msec and a List of 10000 items the Interface approach is 7 seconds faster. At least on my machine – Mong Zhu Feb 14 '17 at 17:30
  • @Mong Zhu..Thank u so much..It is affordable..anyway i will consider both approach – vmb Feb 14 '17 at 17:33
  • @Mong Zhu ..How u feel if it use dynamic keyword..Perfomance impact is same as refelction ?? Parallel.ForEach(T, (currentItem) => { dynamic obj = currentItem; int custId = obj.CustID }); – vmb Feb 14 '17 at 17:34
  • @vmb the dynamic version is as fast as the reflection method 17510 msec – Mong Zhu Feb 14 '17 at 17:35
  • @vmb I made an edit and posted the benchmark. You can try it on your own and see for yourself. Have fun and I hope it helps to decide – Mong Zhu Feb 14 '17 at 17:40
  • @vmb Parallel.Foreach seems to be very inconsistent in the runtime duration. It varies a lot. I would test it against a normal foreach and compare the times. If you have a list of 10000 items, this would make a lot of threads and a lot of managing, which in the end might even result in a slower execution time than a normal foreach – Mong Zhu Feb 15 '17 at 11:49
  • @Mong Zhu ..Thanks for sharing this info – vmb Feb 15 '17 at 18:09
  • @Mong Zhu . I have asked one more question related to this..can u pls help on this..http://stackoverflow.com/questions/42492382/how-to-filter-a-collection-inside-a-generic-method/42492511 – vmb Feb 27 '17 at 18:16
  • It seems the "GetValue" method has been changed in the upper C# version (MSVS2015). No one of its overloads accepts one Arg. However it's said in the doc /* Optional index values for indexed properties. This value should be null for non-indexed properties*/ however MSVS mark it as ERROR BUT IT WORKS. public virtual object GetValue(object obj, object[] index); public abstract object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture); – AbbasAli Hashemian Apr 03 '19 at 12:21
  • 1
    @Alix sorry, but I cannot reproduce this. I tested it in VS2017 and it works fine. No error `PropertyInfo.GetValue(Object object)` works as expected. – Mong Zhu Apr 03 '19 at 13:11
23

Introduce interface:

 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)
     });
 }
Backs
  • 24,430
  • 5
  • 58
  • 85
  • Or a base class. +1, wanted to write the same solution. – Christian Gollhardt Feb 14 '17 at 16:49
  • @ChristianGollhardt it depends. I think, here interface is more suitable as classes A and B are so simple. – Backs Feb 14 '17 at 16:50
  • 3
    You were fast. But why is this method now still generic? You could completely replace `T` with `ICust`. – René Vogt Feb 14 '17 at 16:50
  • @Backs .. I can't use interface approach.Both class A& B are serializable classes – vmb Feb 14 '17 at 16:53
  • 1
    @vmb and what? Serialization and interface are.... not connected at all – Backs Feb 14 '17 at 16:54
  • @vmb use the datacontract serializer, if you need serialization via interfaces. But only because you introduce an interface, doesn't mean you need to use it for serialization. – Christian Gollhardt Feb 14 '17 at 16:57
  • @Backs ..But i need this Param1 after GetDetails()..Can u pls check my updated question – vmb Feb 14 '17 at 16:59
  • @vmb made answer with generic again – Backs Feb 14 '17 at 17:10
  • @vmb yes, method is generic. What problem do you have? – Backs Feb 14 '17 at 17:15
  • @MongZhu what does it mean? If I want to have `public` interface, what will happen? – Backs Feb 15 '17 at 10:25
  • 1
    it means that [explicit interface implementation does not let you specify any access modifiers](http://stackoverflow.com/a/1077827/5174469) – Mong Zhu Feb 15 '17 at 11:40
  • @Backs ..As many suggests ur approach may be better and efficient..but i dont want to change my class structure ..So i will mark Mong Zhu's solution as accepted .Thank u so much for ur quick and brilliant answer – vmb Feb 25 '17 at 08:40
  • @vmb no problem! Different solutions are suitable for different cases. Just to notice - reflection can be a problem in perfomance. But if you have little data - it's not a problem. – Backs Feb 25 '17 at 11:17
  • @Backs.. Thanks brother..i will check – vmb Feb 28 '17 at 11:45
7

If you can't introduce interface or base class on your existing classes, another approach is to use dynamic:

public void ProceesData<T>(IList<T> param1, string date1)
{
    Parallel.ForEach(param1, (currentItem) =>
    {
          dynamic obj = currentItem; 
          int custId = obj.CustID ;
    });
}
Perfect28
  • 11,089
  • 3
  • 25
  • 45
  • ..is any perfomance impact on this ??..since we are using dynamic keyword ?? – vmb Feb 14 '17 at 17:29
  • @vmb : yes indeed, but it's just slightly more than reflection considering it's the case "If you can't introduce interface or base class " – Perfect28 Feb 14 '17 at 17:51
1

Inheritance will work

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

Then rather than a generic method use

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

dynamic

should fix this issue, don't cast or go around in circles..

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