none
Generic Method to Operate on Lists of Different Type RRS feed

  • Question

  • Hello, 

    I am trying to write a generic method that will operate on Lists of different types.  

    public class MyObjectType1
    {
    
      public string OrderName { set; get; } 
      public double OrderPrice {set; get; } 
      public string SpecialObjectType1Var { set; get; }
    }
    
    public class MyObjectType2 
    {
      public string OrderName { set; get; } 
      public double OrderPrice {set; get; } 
      public string SpecialObjectType2Var { set; get; }
    }
    
    
    public static void Main() 
    {
      public void ChangeEitherList<T>(IEnumerable<T> list, int index, Order someOrder)
      {
        list[index].OrderName = order.OrderName; 
        list[index].OrderPrice = order.Price; 
      }
      List<MyObjectType1> myObjectsType1List = new List<MyObjectType1> {OrderName = "Type1OrderName"}; 
      List<MyObjectType2> myObjectsType2List = new List<MyObjectType2> {OrderName = "Type2OrderName"} ; 
    
      ChangeEitherList(list <myObjectsType2List>, 1, order);  
    
    
    }
    



    • Edited by tornadoatc2 Sunday, August 19, 2018 4:34 PM
    Sunday, August 19, 2018 4:33 PM

Answers

  • In the method call to ChangeEitherList you're trying to pass a type name (or something like that). That isn't necessary. Based upon the generic method you created you can pass any enumerable list.

    var items = new [] { 10, 20, 30 };
    ChangeEitherList(items, 1, order);
    
    //Or
    
    ChangeEitherList(myObjectsType1List, 1, order);
    
    

    However your generic method won't work because T is of type Object and Object doesn't have an OrderName or OrderPrice property. If you need to be able to constrain T to be only of specific types (so you can use members other than those defined by Object) then you need to specify the constraint. The constraint allows for some flexibility in what you can limit it to (classes, types with default constructors, etc) but the most common is to specify a base type or interface. The compiler will then ensure that any arguments passed to the method are of a compatible type. This also allows you to use the members of the constraint type in the generic method.

    In your example case neither of your types derive from a shared base type or define an interface so you cannot use a generic method against them. In C# it doesn't matter if they have the same member names, they don't share a base type/interface and therefore are distinct types. The easiest way to fix this is to use an interface.

    public interface IOrderItem
    {
       string OrderName { get; set; }
       double OrderPrice { get; set; }
    }
    
    
    public class MyObjectType1 : IOrderItem
    {
       public string OrderName { get; set; }
       public double OrderPrice { get; set; }
       ...
    }
    
    
    public class MyObjectType2 : IOrderItem
    {
       public string OrderName { get; set; }
       public double OrderPrice { get; set; }
       ...
    }
    
    
    //Generic method for IOrderItem
    public void ChangeEitherList<T> ( IEnumerable<T> list, int index, Order order ) where T: IOrderItem
    {
       //T is typed as IOrderItem so you have access to any of its members
       list[index].OrderName = order.OrderName;
       list[index].OrderPrice = order.Price;
       
       //T does not define this so it is not allowed
       //list[index].SomeMissingProperty = 10;
    }
    
    //Usages
    var myObjectsType1List = new List<MyObjectType1>();
    var myObjectsType2List = new List<MyObjectType2>();
    
    //Allowed because MyObjecType1 implements IOrderItem
    ChangeEitherList(myObjectsType1List, 0, someOrder);
    
    //Allowed because MyObjecType2 implements IOrderItem
    ChangeEitherList(myObjectsType2List, 0, someOrder);
    
    //Not allowed because string does not implement IOrderItem
    var names = new List<string>();
    //ChangeEitherList(names, 0, someOrder);


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by tornadoatc2 Sunday, August 19, 2018 8:20 PM
    • Unmarked as answer by tornadoatc2 Sunday, August 19, 2018 8:20 PM
    • Marked as answer by tornadoatc2 Sunday, August 19, 2018 8:27 PM
    Sunday, August 19, 2018 7:30 PM
    Moderator

All replies

  • hello,

    I don't know if i m wrong, are you programming a method that change items type, a kind of converter ? 

    public IEnumerable<TDestination> ChangeType<TSource, TDestination>(IEnumerable<TSource> source)
        where TDestination: new()
    {
        var resultToReturn = new List<TDestination>();
        var sourceProperties = typeof(TDestination).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.Name);
        var destinatiotnProperties = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(p => p.Name);
        var properties = sourceProperties.Intersect(destinatiotnProperties);
        foreach (var current in source)
        {
            var temp = new TDestination();
            foreach(var property in properties)
            {
                var value = current.GetType().GetProperty(property).GetValue(current);
                temp.GetType().GetProperty(property).SetValue(temp, value);
            }
            resultToReturn.Add(temp);
        }
        return resultToReturn;
    
    }
    
    public class Main
    {
        public void Test()
        {
            var listOfObj1 = new List<MyObjectType1>() {
                new MyObjectType1
                {
                    OrderName = "test",
                    OrderPrice = 12,
                    SpecialObjectType1Var = ""
                }
            };
            var listOfObj2 = ChangeType<MyObjectType1, MyObjectType2>(listOfObj1); ;
        }
    }
    

    I didn't test it yet!

    also consider using a kind TypeConverter definition, based on  which to convert your objects.

    hope it help;


    Sunday, August 19, 2018 6:51 PM
  • In the method call to ChangeEitherList you're trying to pass a type name (or something like that). That isn't necessary. Based upon the generic method you created you can pass any enumerable list.

    var items = new [] { 10, 20, 30 };
    ChangeEitherList(items, 1, order);
    
    //Or
    
    ChangeEitherList(myObjectsType1List, 1, order);
    
    

    However your generic method won't work because T is of type Object and Object doesn't have an OrderName or OrderPrice property. If you need to be able to constrain T to be only of specific types (so you can use members other than those defined by Object) then you need to specify the constraint. The constraint allows for some flexibility in what you can limit it to (classes, types with default constructors, etc) but the most common is to specify a base type or interface. The compiler will then ensure that any arguments passed to the method are of a compatible type. This also allows you to use the members of the constraint type in the generic method.

    In your example case neither of your types derive from a shared base type or define an interface so you cannot use a generic method against them. In C# it doesn't matter if they have the same member names, they don't share a base type/interface and therefore are distinct types. The easiest way to fix this is to use an interface.

    public interface IOrderItem
    {
       string OrderName { get; set; }
       double OrderPrice { get; set; }
    }
    
    
    public class MyObjectType1 : IOrderItem
    {
       public string OrderName { get; set; }
       public double OrderPrice { get; set; }
       ...
    }
    
    
    public class MyObjectType2 : IOrderItem
    {
       public string OrderName { get; set; }
       public double OrderPrice { get; set; }
       ...
    }
    
    
    //Generic method for IOrderItem
    public void ChangeEitherList<T> ( IEnumerable<T> list, int index, Order order ) where T: IOrderItem
    {
       //T is typed as IOrderItem so you have access to any of its members
       list[index].OrderName = order.OrderName;
       list[index].OrderPrice = order.Price;
       
       //T does not define this so it is not allowed
       //list[index].SomeMissingProperty = 10;
    }
    
    //Usages
    var myObjectsType1List = new List<MyObjectType1>();
    var myObjectsType2List = new List<MyObjectType2>();
    
    //Allowed because MyObjecType1 implements IOrderItem
    ChangeEitherList(myObjectsType1List, 0, someOrder);
    
    //Allowed because MyObjecType2 implements IOrderItem
    ChangeEitherList(myObjectsType2List, 0, someOrder);
    
    //Not allowed because string does not implement IOrderItem
    var names = new List<string>();
    //ChangeEitherList(names, 0, someOrder);


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by tornadoatc2 Sunday, August 19, 2018 8:20 PM
    • Unmarked as answer by tornadoatc2 Sunday, August 19, 2018 8:20 PM
    • Marked as answer by tornadoatc2 Sunday, August 19, 2018 8:27 PM
    Sunday, August 19, 2018 7:30 PM
    Moderator