Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Implementation of Generic Method in List<Class>

Implementation of Generic Method in List<Class>

Scheduled Pinned Locked Moved C#
helpwpfarchitecturetutorial
14 Posts 5 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E ezazazel

    Hi guys! I'm dealing with a problem I can't find a solution for. What I'm trying to do is this: I have a Model which should contain a generic method (generic input and return value). And furthermore these Models should be merged into a List. And here it gets complicated and I end up in a dead end. Here's the code which hopefully makes it more clear:

    class MainClass
    {
    public static void Main (string[] args)
    {
    List containers = new List();
    containers.Add(new ContainerA());
    containers.Add(new ContainerB());

    	}
    }
    
    abstract class ContainerBase
    {
    	public string Name {
    		get;
    		set;
    	}
    
    	public abstract TResult Calculate(params TInput\[\] input);
    }
    
    class ContainerA : ContainerBase
    {
    	#region implemented abstract members of Generics.ContainerBase
    	public override TResult Calculate (params TInput\[\] input) // !! public override double Calculate(params int\[\] input)
    	{
    		int\[\] ints = input as int\[\];
    
    		double result = 0;
    		return result; // !! throws an error
    		return result as TResult; // !! doesn't work either
    	}
    	#endregion
    }
    
    class ContainerB : ContainerBase
    {
    	#region implemented abstract members of Generics.ContainerBase
    	public override TResult Calculate (params TInput\[\] input) // !! public override uint Calculate(params uint\[\] input)
    	{
    		throw new System.NotImplementedException ();
    	}
    	#endregion
    }
    

    This is needed because I want to be able to combine different algorithms (presented by the Model), e.g. Algo 1: combine 3 integers, Algo 2: divide two integers, Algo 3: get the sort via Drag&Drop (in MVVM - hence the unified approach). An Alternative would be to store a whole Metod in a Property in the Model, but it needs to be generic as well. And I have no idea how to do that either. Help would be appreciated!

    L Offline
    L Offline
    Luc Pattyn
    wrote on last edited by
    #5

    I'm not a generics specialist, however IMO what you want can't be handled by C# (or any other .NET language) at the moment. IIRC what you want falls under covariance and contravariance and its support is quite limited, see e.g. here[^]. The easiest statement on the subject is this:

    List<TextBox> textboxes=new List<TextBox>();
    List<Control> controls=textboxes;

    will not be acceptable: even though a TextBox is a special Control, a list of them can't be "generalized". Same for method return values. :)

    Luc Pattyn [My Articles] Nil Volentibus Arduum

    E 1 Reply Last reply
    0
    • L Luc Pattyn

      won't work without type limitations, you can't convert just anything to double. And I'm afraid the whole thing cannot be done in C#. :)

      Luc Pattyn [My Articles] Nil Volentibus Arduum

      P Offline
      P Offline
      Pete OHanlon
      wrote on last edited by
      #6

      Yeah, I've just looked at his sample and noticed that he declared his value as a double. Too busy looking at the comments that sat "this doesn't work".

      *pre-emptive celebratory nipple tassle jiggle* - Sean Ewington

      "Mind bleach! Send me mind bleach!" - Nagy Vilmos

      CodeStash - Online Snippet Management | My blog | MoXAML PowerToys | Mole 2010 - debugging made easier

      1 Reply Last reply
      0
      • L Luc Pattyn

        I'm not a generics specialist, however IMO what you want can't be handled by C# (or any other .NET language) at the moment. IIRC what you want falls under covariance and contravariance and its support is quite limited, see e.g. here[^]. The easiest statement on the subject is this:

        List<TextBox> textboxes=new List<TextBox>();
        List<Control> controls=textboxes;

        will not be acceptable: even though a TextBox is a special Control, a list of them can't be "generalized". Same for method return values. :)

        Luc Pattyn [My Articles] Nil Volentibus Arduum

        E Offline
        E Offline
        ezazazel
        wrote on last edited by
        #7

        As an alternative, is there a chance of storing different kind of functions in a property? Something like Func and Func? Guess it's the same problem,but better asking before throwing the idea over board. Do you have an approach on how this can be achieved? Something that didn't come into my mind? Thank you!

        T 1 Reply Last reply
        0
        • E ezazazel

          Hi guys! I'm dealing with a problem I can't find a solution for. What I'm trying to do is this: I have a Model which should contain a generic method (generic input and return value). And furthermore these Models should be merged into a List. And here it gets complicated and I end up in a dead end. Here's the code which hopefully makes it more clear:

          class MainClass
          {
          public static void Main (string[] args)
          {
          List containers = new List();
          containers.Add(new ContainerA());
          containers.Add(new ContainerB());

          	}
          }
          
          abstract class ContainerBase
          {
          	public string Name {
          		get;
          		set;
          	}
          
          	public abstract TResult Calculate(params TInput\[\] input);
          }
          
          class ContainerA : ContainerBase
          {
          	#region implemented abstract members of Generics.ContainerBase
          	public override TResult Calculate (params TInput\[\] input) // !! public override double Calculate(params int\[\] input)
          	{
          		int\[\] ints = input as int\[\];
          
          		double result = 0;
          		return result; // !! throws an error
          		return result as TResult; // !! doesn't work either
          	}
          	#endregion
          }
          
          class ContainerB : ContainerBase
          {
          	#region implemented abstract members of Generics.ContainerBase
          	public override TResult Calculate (params TInput\[\] input) // !! public override uint Calculate(params uint\[\] input)
          	{
          		throw new System.NotImplementedException ();
          	}
          	#endregion
          }
          

          This is needed because I want to be able to combine different algorithms (presented by the Model), e.g. Algo 1: combine 3 integers, Algo 2: divide two integers, Algo 3: get the sort via Drag&Drop (in MVVM - hence the unified approach). An Alternative would be to store a whole Metod in a Property in the Model, but it needs to be generic as well. And I have no idea how to do that either. Help would be appreciated!

          D Offline
          D Offline
          DaveyM69
          wrote on last edited by
          #8

          Your Calculate method is seriously flawed. You want to use a generic parameter and return type but then fix the types inside the method implementation to int and double! You can use type constraints to ensure that any properties/methods that you need (for conversion or whatever) are supported by the types specified.

          Dave
          Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
          BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

          T 1 Reply Last reply
          0
          • E ezazazel

            As an alternative, is there a chance of storing different kind of functions in a property? Something like Func and Func? Guess it's the same problem,but better asking before throwing the idea over board. Do you have an approach on how this can be achieved? Something that didn't come into my mind? Thank you!

            T Offline
            T Offline
            Trak4Net
            wrote on last edited by
            #9

            This works for me... not sure if casting to object will cause you any issues or not.

            public TResult Calculate<TResult, TInput>(params TInput[] input) // !! public override double Calculate(params int[] input)
            {
            int[] ints = input as int[];

                    double result = 0;
            
                    byte test = 4;
            
                    result = test << 2;
            
                    return (TResult)(object)result; // cast to object then generic no errors and this sample code runs and returns the expected value of 16
                }
            
            1 Reply Last reply
            0
            • D DaveyM69

              Your Calculate method is seriously flawed. You want to use a generic parameter and return type but then fix the types inside the method implementation to int and double! You can use type constraints to ensure that any properties/methods that you need (for conversion or whatever) are supported by the types specified.

              Dave
              Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
              BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

              T Offline
              T Offline
              Trak4Net
              wrote on last edited by
              #10

              I agree, unfortunately you can't restrict to double, int etc. as far as I know. Not sure why he needs it to be generic, suppose he could validate the types being passed in to verify if the generic values coming in are of type int, double etc.

              D 1 Reply Last reply
              0
              • E ezazazel

                Hi guys! I'm dealing with a problem I can't find a solution for. What I'm trying to do is this: I have a Model which should contain a generic method (generic input and return value). And furthermore these Models should be merged into a List. And here it gets complicated and I end up in a dead end. Here's the code which hopefully makes it more clear:

                class MainClass
                {
                public static void Main (string[] args)
                {
                List containers = new List();
                containers.Add(new ContainerA());
                containers.Add(new ContainerB());

                	}
                }
                
                abstract class ContainerBase
                {
                	public string Name {
                		get;
                		set;
                	}
                
                	public abstract TResult Calculate(params TInput\[\] input);
                }
                
                class ContainerA : ContainerBase
                {
                	#region implemented abstract members of Generics.ContainerBase
                	public override TResult Calculate (params TInput\[\] input) // !! public override double Calculate(params int\[\] input)
                	{
                		int\[\] ints = input as int\[\];
                
                		double result = 0;
                		return result; // !! throws an error
                		return result as TResult; // !! doesn't work either
                	}
                	#endregion
                }
                
                class ContainerB : ContainerBase
                {
                	#region implemented abstract members of Generics.ContainerBase
                	public override TResult Calculate (params TInput\[\] input) // !! public override uint Calculate(params uint\[\] input)
                	{
                		throw new System.NotImplementedException ();
                	}
                	#endregion
                }
                

                This is needed because I want to be able to combine different algorithms (presented by the Model), e.g. Algo 1: combine 3 integers, Algo 2: divide two integers, Algo 3: get the sort via Drag&Drop (in MVVM - hence the unified approach). An Alternative would be to store a whole Metod in a Property in the Model, but it needs to be generic as well. And I have no idea how to do that either. Help would be appreciated!

                T Offline
                T Offline
                Trak4Net
                wrote on last edited by
                #11

                If you are not absolutely required to use generics, another option would be to create a custom class that has implicit operators to all of the value types you want to support (if you added implicit cast to\from double, then double already can be cast to from int implicitly). just a snippet example

                public class testtype
                {
                    //private constructor
                    private testtype()
                    {
                    }
                
                    public Type ValueType { get { return \_type; } } //readonly type
                    public object Value { get { return \_value; } } //readonly value
                
                    private Type \_type = typeof(int); //initally object is integer
                    private object \_value = int.MinValue; //set value to failsafe value
                
                    //convert to testtype
                    public static implicit operator testtype(int m)
                    {
                        testtype t = new testtype();
                        t.\_value = m;
                        t.\_type  = typeof(int);
                        return t;
                    }
                
                    //convert to int
                    public static implicit operator int(testtype m)
                    {
                        if (typeof(int).Equals(m.\_type))
                            return (int)m.\_value;
                
                        //failsafe value
                        return int.MinValue;
                    }
                }
                

                example usage

                testtype v = 1;

                double b = v;

                Food for thought...

                1 Reply Last reply
                0
                • E ezazazel

                  Hi guys! I'm dealing with a problem I can't find a solution for. What I'm trying to do is this: I have a Model which should contain a generic method (generic input and return value). And furthermore these Models should be merged into a List. And here it gets complicated and I end up in a dead end. Here's the code which hopefully makes it more clear:

                  class MainClass
                  {
                  public static void Main (string[] args)
                  {
                  List containers = new List();
                  containers.Add(new ContainerA());
                  containers.Add(new ContainerB());

                  	}
                  }
                  
                  abstract class ContainerBase
                  {
                  	public string Name {
                  		get;
                  		set;
                  	}
                  
                  	public abstract TResult Calculate(params TInput\[\] input);
                  }
                  
                  class ContainerA : ContainerBase
                  {
                  	#region implemented abstract members of Generics.ContainerBase
                  	public override TResult Calculate (params TInput\[\] input) // !! public override double Calculate(params int\[\] input)
                  	{
                  		int\[\] ints = input as int\[\];
                  
                  		double result = 0;
                  		return result; // !! throws an error
                  		return result as TResult; // !! doesn't work either
                  	}
                  	#endregion
                  }
                  
                  class ContainerB : ContainerBase
                  {
                  	#region implemented abstract members of Generics.ContainerBase
                  	public override TResult Calculate (params TInput\[\] input) // !! public override uint Calculate(params uint\[\] input)
                  	{
                  		throw new System.NotImplementedException ();
                  	}
                  	#endregion
                  }
                  

                  This is needed because I want to be able to combine different algorithms (presented by the Model), e.g. Algo 1: combine 3 integers, Algo 2: divide two integers, Algo 3: get the sort via Drag&Drop (in MVVM - hence the unified approach). An Alternative would be to store a whole Metod in a Property in the Model, but it needs to be generic as well. And I have no idea how to do that either. Help would be appreciated!

                  T Offline
                  T Offline
                  Trak4Net
                  wrote on last edited by
                  #12

                  I am having way to much fun now... This is the closest I could come up with to what you want and somewhat providing a safe implementation of it. Obviously you would need to implement some checks to avoid crashing if incorrect values are used and I don't know exactly what kind of calculations you are performing but here is what I got.

                  public override T Calculate<T, P>(params P[] input) where T : IConvertible where P : IConvertible // !! public override double Calculate(params int[] input)
                  {
                  TypeCode mode = Type.GetTypeCode(typeof(P));

                  switch (mode)
                  {
                      case TypeCode.Double:
                          //do your calculations for double\[\]
                  
                                  //change array to usable format - we know T is double so we can cast them
                                  double\[\] args = (double\[\])Convert.ChangeType(input, typeof(double\[\])); 
                  
                                  //do some math...
                                  double r = 0;
                                  //obviously you want to verify 
                                  r = args\[0\] + args\[1\];
                  
                                  return (T)Convert.ChangeType(r, typeof(T));
                      case TypeCode.Int32:
                          //do your calculations for int32\[\]
                  
                      case TypeCode.Decimal:
                          //do your calculations for decimal\[\]
                  
                      default:
                          break;
                  }
                  
                  throw new NotSupportedException("Generic type value \[" + typeof(T).ToString() + "\] is not supported.");
                  

                  }

                  My test usage:

                  double y = Calculate<int, double>(new double[] { 1.5, 2.25 });

                             Console.WriteLine(y.ToString());
                  
                             try
                             {
                                 Calculate<string, string>(new string\[\] { "no", "gonna error!" });
                             }
                             catch (NotSupportedException ex)
                             {
                                 System.Diagnostics.Trace.TraceError(ex.Message);
                             }
                  

                  The trace output from throwing in a string value

                  Quote:

                  UnitTest.vshost.exe Error: 0 : Generic type value [System.String] is not supported.

                  E 1 Reply Last reply
                  0
                  • T Trak4Net

                    I agree, unfortunately you can't restrict to double, int etc. as far as I know. Not sure why he needs it to be generic, suppose he could validate the types being passed in to verify if the generic values coming in are of type int, double etc.

                    D Offline
                    D Offline
                    DaveyM69
                    wrote on last edited by
                    #13

                    On the rare times I've needed to restrict to a few types, I've created an interface with the required properties/methods and then created concrete wrapper classes around the required type - with implicit conversions - that implement the interfaces. The interface can then be used as a type constraint and all is well in the world :) Edit: Just read your replies below - pretty much where you're heading too I think ;)

                    Dave
                    Binging is like googling, it just feels dirtier. Please take your VB.NET out of our nice case sensitive forum. Astonish us. Be exceptional. (Pete O'Hanlon)
                    BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)

                    1 Reply Last reply
                    0
                    • T Trak4Net

                      I am having way to much fun now... This is the closest I could come up with to what you want and somewhat providing a safe implementation of it. Obviously you would need to implement some checks to avoid crashing if incorrect values are used and I don't know exactly what kind of calculations you are performing but here is what I got.

                      public override T Calculate<T, P>(params P[] input) where T : IConvertible where P : IConvertible // !! public override double Calculate(params int[] input)
                      {
                      TypeCode mode = Type.GetTypeCode(typeof(P));

                      switch (mode)
                      {
                          case TypeCode.Double:
                              //do your calculations for double\[\]
                      
                                      //change array to usable format - we know T is double so we can cast them
                                      double\[\] args = (double\[\])Convert.ChangeType(input, typeof(double\[\])); 
                      
                                      //do some math...
                                      double r = 0;
                                      //obviously you want to verify 
                                      r = args\[0\] + args\[1\];
                      
                                      return (T)Convert.ChangeType(r, typeof(T));
                          case TypeCode.Int32:
                              //do your calculations for int32\[\]
                      
                          case TypeCode.Decimal:
                              //do your calculations for decimal\[\]
                      
                          default:
                              break;
                      }
                      
                      throw new NotSupportedException("Generic type value \[" + typeof(T).ToString() + "\] is not supported.");
                      

                      }

                      My test usage:

                      double y = Calculate<int, double>(new double[] { 1.5, 2.25 });

                                 Console.WriteLine(y.ToString());
                      
                                 try
                                 {
                                     Calculate<string, string>(new string\[\] { "no", "gonna error!" });
                                 }
                                 catch (NotSupportedException ex)
                                 {
                                     System.Diagnostics.Trace.TraceError(ex.Message);
                                 }
                      

                      The trace output from throwing in a string value

                      Quote:

                      UnitTest.vshost.exe Error: 0 : Generic type value [System.String] is not supported.

                      E Offline
                      E Offline
                      ezazazel
                      wrote on last edited by
                      #14

                      That's pretty cool - thanks your your support. That and your first sample helped me to got it solved. First I used a few reflections for the type passing, but now I think I'm going to make use of your implementation. Thank you again!

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups