【问题标题】:Template functions and types in C#C# 中的模板函数和类型
【发布时间】:2014-02-08 13:33:16
【问题描述】:

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

A 类有一个私有数组列表,它应该被 B 类的实例填充。 因此,A 类的任何实例都将在 B 类元素的 ArrayList 上。 类 A 具有向其 ArrayList 添加 B 的新实例的功能。更具体地说,在 A.Add 内部,一个新的 B 被实例化、初始化,然后添加到 ArrayList。 B 类是一个非常简单的类:它有一个枚举类型,并且它应该 还包含对外部对象/类或其他的引用。 假设有 * appleClass、peachClass、lemonClass * 类,classB 是fruit 类,class 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 中,“水果”的一部分来自 Unity 类,其他一些“水果”是我自己的类。这意味着我不能为来自 Unity 命名空间的那些声明父类。我更希望能够对 "fruit" 进行通用引用,这将是旧 C 中的 void 指针。我知道 C# 是强类型的,并且不允许大多数类似指针的用途,但必须有我一种将“未知”对象引用传递给类/函数并稍后使用/解决它的方法...

【问题讨论】:

    标签: c# template-function


    【解决方案1】:

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

    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
    

    旧答案:

    您可以简单地将fruit 设为抽象类或接口,让所有内容都继承/实现它并保留一个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);
        }
    }
    

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

    但如果你需要这样做,那么你可能是从错误的角度来处理问题。

    【讨论】:

    • 感谢@BlackBear 的回答。另一件事:如果我使用您建议的这种方式,当我从 A 中返回列表(在我的主代码中)时,我需要知道每个元素的确切类型(水果)。这可能吗?我不想只知道它是“水果”类型。我想知道它是柠檬还是苹果。例如:Lista=Basket.GetFruits(); switch(a[0].c) { case 0: { Lemon L= a[0].f;// 填这个可以吗??? f 会是柠檬类型吗? } //.... }
    • @Sagi2313 我花了一段时间,但你仍然可以做你想做的事,而不会让人头疼。请查看我的编辑
    • 再次感谢您的回答。我只是想出了一个方法,我只是想描述它,并得到你的评论。我什至不知道在 C# 中做我认为的事情是否合法,我认为 C++ 允许这样做。就是这样:我找到了一种创建抽象类的方法。在其中我有一个枚举,它存储每个实例的派生类的类型。所以所有“水果类”都将从父类派生,并在其构造函数中设置它们的类型。稍后我会将父实例添加到“篮子”,并使用强制转换来解析实际实例。类抽象父 { }
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        再次感谢您的回答。我只是想出了一个方法,我只是想描述它,并得到你的评论。我什至不知道在 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”并确定该父代是什么水果。 如果这行得通(就像现在一样,或稍作改动),它对我有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-15
          • 2011-03-04
          • 1970-01-01
          • 1970-01-01
          • 2010-12-27
          • 2018-05-25
          • 1970-01-01
          相关资源
          最近更新 更多