Casting in a generic abstract class that implements an interface

Given the base class of Coin

 public class Coin { } 

and two derived classes Coin50Cent and Coin25Cent

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

The challenge is to create a CoinMachine object (used for exchanging coins, for example, if 50 percent of the coins return two coins of 25 cents), which meet the following requirements:

  • CoinMachine must have two collections of Coin25Cent and Coin50Cent .

  • Collections should be derived from the abstract generic class CoinStack<T> , which has two methods

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

  • CoinMachine should work as follows

     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 

Here's my implementation. It looks like I'm having a cast problem in the abstract CoinStack class.

 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); } } } 
+7
generics c #
source share
3 answers

Your problem is that ICoinStack has common methods like Push<T>(T item) , which basically says that the implementation of ICoinStack can accept item any type.

However, in your CoinStack<T> implementation, you want to restrict <T> from ICoinStack.Push<T> to <T> from CoinStack<T> . The compiler should have given you a warning indicating that the type parameter T has the same name as the type parameter T external type.

You must fix your design by making ICoinStack itself generic (as in ICoinStack<T> ), or change its method to accept / return an object (or even better: Coin ) instead of T

Example:

 // accept/return Coin instead of T to keep ICoinStack // and it 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); } } 
+4
source share

The solution is to change this code:

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

:

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

and implementations as follows:

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

The problem was that your code allowed you to use one instance of CoinStack with different Coin implementations. When you specify generics for the whole type (the whole CoinStack<T> , you use the same type that will be used in class methods ( Push and Pop will only accept the same T, not any type, also note that they no more <T> needed.)

Also note the general type restrictions (see also my comment below your question). These restrictions ensure that you only call Push and Pop with an instance of the Coin class (opposite to the previous code, which can have any type of gutter passed in) and thus have improved type safety.

In your CoinMachine class CoinMachine you must edit the Push and Pop methods as follows:

  // 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); } 
0
source share

Here is how I would decide:

 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); } } 
0
source share

All Articles