C#中的模板函数和类型

时间:2014-02-08 13:33:16

标签: c# template-function

您好,因为涉及的类型,我在设置课程时遇到了一些困难。 这是想法:

A类有一个私有的arraylist,它应该被B类的实例填充。 因此,类A的任何实例都将具有类B类元素的ArrayList。 类A具有向其ArrayList添加B的新实例的功能。更具体地说,在A.Add中,实例化新B并初始化,然后添加到ArrayList。 B类是一个非常简单的类:它有一个枚举类型,应该还包含对外部对象/类或其他的引用。 假设有类* appleClass,peachClass,lemonClass *和classB是fruit类,而类A是fruitBasket。每当主代码看到一个水果时,它应该要求A级将其添加到篮子中。这意味着,A.add( thisfruit ),但这种水果可能是任何水果类。这反过来意味着我需要一个通用的签名来实例化classB,并将它添加到A.arraylist。我不知道我是否有任何意义,但我也会尝试提供一些代码来解释我的问题。

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

我应该如何使用模板?我知道上面的代码是错误的,但我无法想象这是如何做得最好的。

谢谢

亚历

修改的 谢谢大家的答案。它们似乎都指向使用基类/抽象类Fruit,并从中获取特定的水果。我很遗憾地告诉你,我的实际项目不涉及水果,我只是为了简单而使用它们。实际项目是在 Unity 中,而“水果”的somr来自Unity类,其他一些“水果”是我自己的类。这意味着我无法为Unity的命名空间声明父类。我更希望能够对“水果”有一个通用的引用,这将成为旧C中的无效指针。我知道C#是强类型的,并且不会允许大多数类似指针的用法,但必须我可以将“未知”引用对象传递给类/函数并稍后使用/解析...

4 个答案:

答案 0 :(得分:3)

编辑以反映您问题中的新信息: ArrayList是一种很好的方法,但在这种情况下我不会使用泛型,因为它会导致很多麻烦和代码重复。相反,我会使用反射在运行时中查找并调用正确的重载。一段代码值得千言万语,所以这里有一个简单的控制台应用程序来显示:

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

这是输出:

1
2
3

旧答案:

你可以简单地将水果变成抽象类或接口,让所有东西继承/实现它并保留水果列表:

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

可以通过运算符is了解从列表中获取的元素的确切类型:

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

或者,您可以使用as运算符从IFruit转换为其实现之一(注意,如果转换不可能,结果将为null,即您使用了错误的类型):

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

但是如果你需要这样做,那么也许你是从错误的角度来解决问题。

答案 1 :(得分:1)

C#没有模板,但确实有generics,这是一个类似的概念。你非常接近,但你的Fruit类根本不需要是通用的。只需确保所有LemonApplePeach类都继承自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() { } 
}

现在,您可以使用generic constraint这样设计Basket课程:

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

答案 2 :(得分:0)

在C#中,它们被称为通用方法而非模板方法,因为它们不是每次在编译时生成的。

至于你的例子:

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

答案 3 :(得分:0)

再次感谢您的回答。我想出了一个方法,我只想描述一下,然后对它进行评论。我甚至不知道在C#中做我想的是否合法,我认为C ++允许这样做。 这里是: 我找到了一种创建抽象类的方法。在其中我有一个枚举,它存储每个实例的派生类的类型。所以所有“水果类”都将从父类派生,并在其构造函数中设置它们的类型。稍后我将把父实例添加到“Basket”中,并使用强制转换来解析实际的实例。

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?

   }
}

当我需要回读篮子的内容时,我会将其作为父母阅读,检查“myType”并确定父母的果实。 如果这样做(现在,或者稍作修改),它对我有用。