【问题标题】:C# - Stack Push operation does not work even after Correct Generic constraintC# - 即使在正确的通用约束之后,堆栈推送操作也不起作用
【发布时间】:2019-09-10 08:55:28
【问题描述】:
namespace ConsoleApp3
{
    class Program
    {
        // Main method - entry point of program
        static void Main(string[] args)
        {
            var animals = new Stack<Animal>();
            ZooCleaner.Wash(animals);
        }
    }

    //Simple classes declared and inherited.
    public class Animal { }
    public class Bear : Animal{ }
    public class Camel : Animal { }

    public class Stack<T> //Basic stack implementation
    {
        int position;
        T[] data = new T[100];
        public void Push(T obj) => data[position++] = obj;
        public T Pop() => data[--position];
    }

    public class ZooCleaner
    {
        public static void Wash<T>(Stack<T> animals) where T:Animal
        {
            //Why I cannot do this? I have correctly stated that 'T' can 
            //be of type Animal or can derive Animal but this
            //still causes compilation error!

            animals.Push(new Animal()); //Error: Cannot convert from 'Animal' To Type T!!
            animals.Push(new Bear()); //Error: Cannot convert from 'Bear' To Type T!!
        }
    }
}

问题:

在 Wash() 方法中,我正确地将通用参数“T”设置为“动物”类型或可以从“动物”派生。那么为什么我不能通过 push 操作来插入 Animal 或 Bear 的对象呢?

为什么animals.Push(new Animal()); animals.Push(new Bear());会导致编译错误?

【问题讨论】:

    标签: c# generics stack generic-constraints


    【解决方案1】:

    这是正常的,也是意料之中的。如果您有Stack&lt;T&gt; where T : Animal,那么您必须想象T 可能类似于Giraffe。唯一允许您推送Stack&lt;Giraffe&gt;GiraffeGiraffeMasaiGiraffeNubianGiraffe 等)。您不能推送BearAnimal:它必须是Giraffe(或更好)。

    对于T,这意味着您可以推送T - 可能是new T()(通过T : new() 约束)。

    如果您希望能够推送任何Animal:不要使用Stack&lt;T&gt; where T : Animal - 使用Stack&lt;Animal&gt;

    【讨论】:

      【解决方案2】:

      欢迎使用 C#。这是 C# 编译器的一个非常烦人的限制。您会期望这适用于任何其他语言,因为它在 OOP 范式中是有意义的。

      OP 我希望您知道 C# 中已经有堆栈的实现。如果它与不接受继承类型有相同的问题,那么您可以使用包含此通用项的包装器。很痛苦,但你选择了 CS。

      所以你必须做一个工作。这也不是一个有趣的解决方法。这里是:

      > public class Stack<T>
        {
          public int position => data.Count - 1;
          IList<T> data = new List<T>();
          public void Push(T obj) => data.Add(obj);
      
          public T Pop()
          {
              var ret = data[position];
              data.RemoveAt(position);
              return ret;
          }
      
          public override string ToString()
          {
              return $"Num of elements: {data.Count}. {data}";
          }
        }
      
      > public class Animal { }
        public class Bear : Animal{ }
        public class Camel : Animal { }
      
      > var sta = new Stack<Animal>();
        sta.Push(new Animal());
        sta.Push(new Bear());
        sta.Push(new Camel());
      
      > sta
      [Num of elements: 3. System.Collections.Generic.List`1[Submission#1+Animal]]
      
      > while (sta.position >=0)
      {
          var x = sta.Pop();
          Console.WriteLine($"Popped element type: {x.GetType()}");
      }
      Popped element type: Submission#3+Camel
      Popped element type: Submission#2+Bear
      Popped element type: Submission#1+Animal
      
      

      如果任何 C# 天才有更好的解决方法,请告诉我,因为这是泛型的一个非常烦人的限制。泛型应该像其他语言一样允许继承类。

      【讨论】:

      • 不,您不会期望它会起作用。 OP 的方法采用Stack&lt;T&gt;,其中TAnimal。因此可以使用Wash(new Stack&lt;Camel&gt;); 调用它。在这种情况下,尝试推送BearAnimal 将是一个错误。因为BearAnimal 都不是Camel
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-03
      • 1970-01-01
      • 2018-06-12
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      • 2017-10-29
      相关资源
      最近更新 更多