Casting in generic abstract class that implements interface
Given a base class Coin
public class Coin { }
and two derived classes Coin50Cent
and Coin25Cent
public class Coin50 : Coin { }
public class Coin25 : Coin { }
The task is to create an object of a CoinMachine
(used for coin exchange, eg puting 50 cent coin returns two 25 cent coins) that corresponds the following requierments:
CoinMachine
must have two collections of Coin25Cent
and Coin50cent
objects.
The collections must be derived from an abstract generic class CoinStack<T>
that has two methods
void Push(T item); T Pop();
CoinMachine
must 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 Seems like I have a problem with casting in 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);
}
}
}
Your problem is that ICoinStack
has generic methods, like Push<T>(T item)
, which basically says that an implementation of ICoinStack
can accept an item
of any type.
However, in your implementation CoinStack<T>
, you want to limit <T>
of ICoinStack.Push<T>
to <T>
of CoinStack<T>
. The compiler should have already given you a warning, saying the type parameter T
has the same name as the type parameter T
of the outer type.
You have to fix your design, either by making ICoinStack
itself generic (as in ICoinStack<T>
), or change it's method to accept/return object
(or even better: Coin
) instead of T
.
Example:
// 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);
}
}
以下是我将如何解决它的方法:
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);
}
}
The solution would be to change this code:
public interface ICoinStack
{
T Pop<T>();
void Push<T>(T item);
}
to this:
public interface ICoinStack
{
Coin Pop();
void Push(Coin item);
}
and the implementation 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 to use a single CoinStack
instance with different Coin
implementations. When you specify generics for the entire type (the entire CoinStack<T>
, you enforce the same type to be used within the class methods ( Push
and Pop
will only accept the same T, not any type, also notice they do not need a <T>
anymore.)
Notice also the generic type constraints (see also my comment below your question). These constrains ensure that you only call Push
and Pop
with an instance of the Coin
class (opposing to the previous code that can have any type passed trough), and thus, have improved type safety.
In your CoinMachine
class, 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);
}
链接地址: http://www.djcxy.com/p/75922.html
上一篇: 使用mysql Prepared Statement进行冒号分隔查询
下一篇: 在实现接口的通用抽象类中进行投射