【问题标题】:Casting in generic abstract class that implements interface铸造实现接口的通用抽象类
【发布时间】:2013-12-04 10:21:24
【问题描述】:

给定一个基类Coin

public class Coin { }

还有两个派生类Coin50CentCoin25Cent

public class Coin50 : Coin { }
public class Coin25 : Coin { }

任务是创建一个CoinMachine 的对象(用于硬币交换,例如投入 50 美分硬币返回两个 25 美分硬币),它对应于以下要求:

  1. CoinMachine 必须有两个 Coin25CentCoin50cent 对象集合。

  2. 集合必须派生自具有两个方法的抽象泛型类CoinStack<T>

    void Push(T item); T Pop();

  3. CoinMachine 必须按以下方式工作

    CoinMachine.Push(new Coin25()); // puts 25cent in 25c stack
    CoinMachine.Push(new Coin50()); // puts 50cent in 50c stack
    CoinMachine.Pop();      // gets 50cent from 50c stack
    CoinMachine.Pop();      // gets 25cent from 25c stack
    

这是我的实现 好像我在抽象 CoinStack 类中的转换有问题。

namespace ConsoleApplication1
{
    /* Given condition */
    class Program
    {
        static void Main(string[] args)
        {
            CoinMachine.Push(new Coin25());
            CoinMachine.Push(new Coin50());
            CoinMachine.Pop<Coin50>();
            CoinMachine.Pop<Coin25>();
        }
    }

    public class Coin { }
    public class Coin50 : Coin { }
    public class Coin25 : Coin { }
    /* End given condition */

    public interface ICoinStack
    {
        T Pop<T>();
        void Push<T>(T item);
    }

    /* The problem within this abstract class */
    public abstract class CoinStack<T> : ICoinStack
    {
        private Queue<T> _stack = new Queue<T>();

        public T Pop<T>() { return _stack.Dequeue(); }
        public void Push<T>(T item) { _stack.Enqueue(item); }
    }

    public class CoinStack50 : CoinStack<Coin50> { }
    public class CoinStack25 : CoinStack<Coin25> { }

    public class CoinMachine
    {
        private static Dictionary<Type, ICoinStack> map;

        static CoinMachine()
        {
            map = new Dictionary<Type, ICoinStack>()
            {
                { typeof(Coin50), new CoinStack50() },
                { typeof(Coin25), new CoinStack25() }
            };
        }

        public static T Pop<T>()
        {
            var type = typeof(T);
            return map[type].Pop<T>();
        }

        public static void Push<T>(T item)
        {
            var type = typeof(T);
            map[type].Push(item);
        }
    }
}

【问题讨论】:

  • 我建议您在 CoinStack&lt;T&gt; 上使用类型约束,如下所示:CoinStack&lt;T&gt; where T: Coin。您还必须在 CoinMachine.PushPop 方法上添加相同的内容。这将增加代码的类型安全性,但我怀疑它是否能解决问题。
  • 您可以只保留Coin 的集合,而不是它们的派生类型。然后您可以做的是将您的 Coin25Coin50 对象推送到该堆栈,然后将它们作为它们的实际超类型检索。
  • @aevitas,我认为 OP 是故意这样做的,因为他/她需要每种硬币类型的单独收藏
  • @Ivaylo Slavov,aevitas 感谢您的回复,但 Ivaylo Slavov 绝对正确。 CoinMachine 必须包含每种硬币类型的单独集合。

标签: c# generics


【解决方案1】:

您的问题是ICoinStack 具有通用方法,例如Push&lt;T&gt;(T item),它基本上表示ICoinStack 的实现可以接受any 类型的item

但是,在您的实现CoinStack&lt;T&gt; 中,您希望将&lt;T&gt;ICoinStack.Push&lt;T&gt; 限制为&lt;T&gt;CoinStack&lt;T&gt;。编译器应该已经给你警告了,说类型参数T和外层类型的类型参数T同名。

你必须修复你的设计,要么通过使ICoinStack 本身成为通用(如ICoinStack&lt;T&gt;),要么改变它的方法来接受/返回object(或者更好:Coin)而不是@987654336 @。

示例:

// accept/return Coin instead of T to keep ICoinStack
// and it's methods non-generic
public interface ICoinStack
{
   Coin Pop();
   void Push(Coin item);
}

// explicit interface implementation and the where T:Coin 
// constrain help us here to implement ICoinStack
public abstract class CoinStack<T> : ICoinStack where T:Coin
{
   private Queue<T> _stack = new Queue<T>();

   Coin ICoinStack.Pop() { return _stack.Dequeue(); }
   void ICoinStack.Push(Coin item) { _stack.Enqueue((T)item); }

   public T Pop() { return _stack.Dequeue(); }
   public void Push(T item) { _stack.Enqueue(item); }
}

// we need a cast in Pop<T>, and also the where T:Coin constrain
public class CoinMachine
{
   private static Dictionary<Type, ICoinStack> map;

   static CoinMachine()
   {
       map = new Dictionary<Type, ICoinStack>()
       {
           { typeof(Coin50), new CoinStack50() },
           { typeof(Coin25), new CoinStack25() }
       };
   }

   public static T Pop<T>() where T:Coin
   {
       var type = typeof(T);
       return (T)map[type].Pop();
   }

   public static void Push<T>(T item) where T:Coin
   {
       var type = typeof(T);
       map[type].Push(item);
   }
}

【讨论】:

    【解决方案2】:

    以下是我的解决方法:

    public class Coin { }
    public class Coin50 : Coin { }
    public class Coin25 : Coin { }
    /* End given condition */
    
    public interface ICoinStack
    {
        T Pop<T>() where T: Coin;
        void Push<T>(T item) where T: Coin;
    }
    
    /* The problem within this abstract class */
    public abstract class CoinStack : ICoinStack
    {
        private Queue<Coin> _stack = new Queue<Coin>();
    
        public TCoin Pop<TCoin>() where TCoin: Coin { return (TCoin)_stack.Dequeue(); }
        public void Push<TCoin>(TCoin item) where TCoin: Coin { _stack.Enqueue(item); }
    }
    
    public class CoinStack50 : CoinStack { }
    public class CoinStack25 : CoinStack { }
    
    public class CoinMachine
    {
        private static Dictionary<Type, ICoinStack> map;
    
        static CoinMachine()
        {
            map = new Dictionary<Type, ICoinStack>()
            {
                { typeof(Coin50), new CoinStack50() },
                { typeof(Coin25), new CoinStack25() }
            };
        }
    
        public static T Pop<T>() where T: Coin
        {
            var type = typeof(T);
            return map[type].Pop<T>();
        }
    
        public static void Push<T>(T item) where T: Coin
        {
            var type = typeof(T);
            map[type].Push(item);
        }
    }
    

    【讨论】:

    • 任务条件要求抽象 CoinStack 类是通用的 :)
    【解决方案3】:

    解决方案是更改此代码:

    public interface ICoinStack
    {
        T Pop<T>();
        void Push<T>(T item);
    }
    

    到这里:

    public interface ICoinStack
    {
        Coin Pop();
        void Push(Coin item);
    }
    

    实现如下:

    public abstract class CoinStack<T> : ICoinStack where T: Coin
    {
        private Queue<T> _stack = new Queue<T>();
    
        public T Pop() { return _stack.Dequeue(); }
        Coin ICoinStack.Pop() {return this.Pop(); }
        public void Push(T item) { _stack.Enqueue(item); }
        void ICoinStack.Push(Coin item) { this.Push((T) item);
    }
    

    问题是您的代码允许使用具有不同 Coin 实现的单个 CoinStack 实例。当您为整个类型(整个CoinStack&lt;T&gt;)指定泛型时,您强制在类方法中使用相同的类型(PushPop 将只接受相同的 T,而不是任何类型,还要注意他们这样做不再需要&lt;T&gt;。)

    还要注意泛型类型约束(另请参阅我在您问题下方的评论)。这些约束确保您仅使用 Coin 类的实例调用 PushPop(与之前可以通过任何类型的代码相反),因此提高了类型安全性。

    在您的CoinMachine 类中,您必须编辑PushPop 方法,如下所示:

        // notice the generic constraint, as it is required now by the compiler
        public static T Pop<T>() where T: Coin 
        {
            var type = typeof(T);
            // we need a cast, as the `ICoinStack` now return `Coin`
            return (T) map[type].Pop();
        }
    
        public static void Push<T>(T item) where T: Coin
        {
            var type = typeof(T);
            map[type].Push(item);
        }
    

    【讨论】:

      猜你喜欢
      • 2012-12-01
      • 1970-01-01
      • 2012-07-17
      • 2015-01-03
      • 1970-01-01
      • 2012-01-20
      • 1970-01-01
      • 2019-05-27
      • 1970-01-01
      相关资源
      最近更新 更多