当前位置: 动力学知识库 > 问答 > 编程问答 >

Template functions and types in C#

问题描述:

Hello I am having some difficulty with setting up a class , because of the types involved.

Here is the Idea:

class A has a private arraylist which is supposed to be filled with instances of class B.

So any instance of class A will have its on ArrayList of class B elements.

class A has a function to add to its ArrayList a new instance of B. To be more specific, inside the A.Add, a new B is instantiated, and initialized, then added to the ArrayList.

class B is a very simple class: It has one enum Type in it, and it should also contain a reference to an external object/class or whatever.

Suppose there are classes * appleClass, peachClass , lemonClass * and classB is the fruit class, and class A is the fruitBasket. The everytime the main code sees a fruit, it should ask class A to add it in the basket. that means, A.add(thisfruit), but this fruit might be any of the fruitclasses. which in turn means that I need a generic signature to instantiate classB , and add it to A.arraylist. I don;t know if I make any sense but I will also try to give some code to explain my problem.

class Lemon

{

//....

}

class Apple

{

//....

}

class Peach

{

//....

}

class Fruit

{

public int color;

<T> fruit;///this should be the <T> type depending on the fruit class i am adding

public Fruit(T f, int c){fruit = f; color = c;}

}

class Basket

{

ArrayList FruitArray= new ArrayList();

public void AddFruit<T>(T f,int c )

{

fru = new Fruit(f,c);

FruitArray.Add(fru);

}

}

SomeHow I should use templates?? I know the code above is wrong, but I can't figure how is this best done.

Thank You

Alex

EDIT

Thank You all for your answers. They all seem to point to using a base/abstract class Fruit, and derive specific fruits from it. I am sorry to tell you that my actuall project doesn't involve fruits, I just used them for simplicity. The actual project is in Unity and somr of the "fruits" are coming from Unity classes , some other "Fruits" are my own classes. this means that I can't declare parent class for the ones that come from Unity's namespace. I was more hopeing to be able to have a generic reference to the "fruit" , that would be a void pointer in good old C. I know C# is strongly typed, and won't allow most pointer-like uses, but there must me a way to pass an "unknown" reference-to-object to a class/function and use/resolve it later...

网友答案:

EDIT to reflect the new information in your question: An ArrayList is a good way to proceed, but I wouldn't use generics in this case since it is going to cause a lot of headaches and code duplication. Instead, I would use reflection to find and invoke the correct overload at runtime. A snippet of code is worth one thousand words, so here's a simple console application to show this:

class ObjectA {
    public int A { get; set; }
    public ObjectA( int a ) {
        this.A = a;
    }
}

class ObjectB {
    public int B { get; set; }
    public ObjectB( int b ) {
        this.B = b;
    }
}

class ObjectC {
    public int C { get; set; }
    public ObjectC( int c ) {
        this.C = c;
    }
}

class DynamicOverloadResolution {
    ArrayList items;

    public DynamicOverloadResolution( ) {
        items = new ArrayList( ) { 
            new ObjectA( 1 ), new ObjectB( 2 ), new ObjectC( 3 )
        };
    }

    public void ProcessObjects( ) {
        foreach ( object o in items )
            processObject( o );
    }

    private void processObject( object o ) {
        Type t = typeof( DynamicOverloadResolution );
        IEnumerable<MethodInfo> processMethods = t.GetMethods( )
            .Where( m => m.Name == "process" );

        foreach(MethodInfo info in processMethods) {
            if ( info.GetParameters( ).First( ).ParameterType == o.GetType( ) )
                info.Invoke( this, new object[ ] { o } );
        }
    }

    public void process( ObjectA a ) { Console.WriteLine( a.A ); }
    public void process( ObjectB b ) { Console.WriteLine( b.B ); }
    public void process( ObjectC c ) { Console.WriteLine( c.C ); }
}

class Program {
    static void Main( string[ ] args ) {
        var d = new DynamicOverloadResolution( );
        d.ProcessObjects( );
    }
}

And here's the output:

1
2
3

Old Answer:

You can simply make fruit an abstract class or an interface, have everything inherit from/implement it and keep a list of fruit:

interface IFruit {
    public int c { get; set; }
}

class Apple : IFruit { }
class Peach : IFruit { }
class Lemon : IFruit { }

class Basket {
    List<IFruit> fruitList = new List<IFruit>();

    public void AddIFruit<T>(int c) 
        where T : IFruit, new {

        var f = new T();
        f.c = c;
        fruitList.Add(f);
    }
}

It is possible to know the exact type of the elements you get out from the list with the operator is:

var f = fruitList.First();
if(f is Apple) { }
else if(f is Peach) { }
else if(f is Lemon) { }

In alternative, you can use the as operator to convert from IFruit to one of its implementations (beware, the result will be null if the conversion is not possible, i.e. you used wrong types):

List<Fruit>a=Basket.GetFruits();
switch(a[0].c) { 
    case 0:
        Lemon L = a[0] as Lemon;
}

But if you need to do this, then maybe you are approaching the problem from a wrong perspective.

网友答案:

C# doesn't have templates, but it does have generics, which is a similar concept. You're pretty close, but your Fruit class doesn't need to be generic at all. Just make sure all your Lemon, Apple and Peach classes inherit from Fruit:

class Fruit
{
    public int color;
    public Fruit() { } // default constructor for generic constrain below
}
class Lemon : Fruit
{
    //....
    public Lemon() : base() { } 
}
class Apple : Fruit
{
    //....
    public Apple() : base() { } 
}
class Peach : Fruit
{
    //....
    public Peach() : base() { } 
}

Now you can design your Basket class with a generic constraint like this:

class Basket
{
    List<Fruit> FruitList = new List<Fruit>();

    public void AddFruit<T>(int c) where T : Fruit, new()
    {
        fru = new T();
        fru.color = c;
        FruitList.Add(fru);
    }
}

Basket basket = new Basket();
basket.Add<Apple>(2);
网友答案:

In C# they are called Generic Methods not Template Methods since they are not generated for each time in compile time.

As for your example:

class Lemon : Fruit
{
    public Lemon(int c = 0) : base(c){}
    //....
}
class Apple : Fruit
{
    public Apple (int c = 0) : base(c){}
    //....
}
class Peach : Fruit
{
    public Peach (int c = 0) : base(c){}
    //....
}

class Fruit
{
    public int color;
    public Fruit(int c){ color = c; }
}

class Basket
{
    List<Fruit> fruits = new List<Fruit>();

    public void AddFruit(Fruit f)
    {
        fruits.Add(f);
    }

    public void CreateFruit<T>(int c) : where T : Fruit
    {
        Fruit f = Activator.CreateInstance<T>() as Fruit;
        f.color = c;
        fruits.Add(f);
    }
}
网友答案:

Thank you again for answering. I just came up with a way, I just want to describe it, and get your comment on it. I don't even know if it is legal to do what i thought in C#, I think C++ allows that. Here it is: I found a way to create an abstract class . In it I have an enum that stores the type of the derived class for each instance. so all "fruit classes" will derive from parent, and set their type in their constructor. later I will be adding parent instances to the "Basket", and use casting to resolve the actual instance.

class abstract parent
{
    public enum fruitType{lemon,apple,microsoft};
    public fruitType myType;
    public int ID;
}

class Lemon : parent
{
   Lemon(int i){ID=i;myType=fruitType.lemon};
   // various Lemon functions
}
class Apple: parent
{
   Apple(int i){ID=i;myType=fruitType.apple};
   // various Apple functions
}

then comes the Array:
class Basket
{
   public List<parent> basket=new List<parent>();
   void AddToBasket(parent p)
   {
    basket.Add(p);
   }

}
And in main:

class pgm
{
   void main()
   {
    Basket bsk=new Basket();
    parent pnt=new parent();
    Lemon  lmn=new Lemon();
    bsk.AddToBasket(lmn);// is this OK?

   }
}

when I need to read back the contents of basket, I will read it as parent, check "myType" and decide what fruit that parent is. If this works (as it is now, or with minor changes) it works for me.

分享给朋友:
您可能感兴趣的文章:
随机阅读: