我有一些类和接口:
interface IAnimal
{
}
class Cat : IAnimal, ILiveInZoo
{
}
interface ILiveInZoo
{
}
另外,我有一些方法和通用方法:
class Context
{
public ILiveInZoo GetWhoLivesInZoo(string name)
{
if (name == "Cat")
return new Cat();
return null;
}
static CompositionContainer Container = null;
public void GiveFood<T>(T animal) where T : IAnimal
{
var methods = Container.GetExports<Action<T, EventArgs>, AttributeMetadata>();
//execute methods
}
}
这是一个用例:
Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
context.GiveFood(animal);
}
正如您在GiveFood方法中所见,我正在使用MEF。在用例中,当我将猫投放到IAnimal时,在GiveFood方法中,typeof(T)将是IAnimal而不是Cat。第一个问题是: cat变量的实例是Cat类。为什么当我投放它时,typeof(T)会是IAnimal。 我的问题是当我将猫转换为IAnimal接口时,在GiveFood方法中,GetExports方法将与IAnimal相关的方法返回给Cat类。我找到了解决该问题的解决方案,它使用了反射:
Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
MethodInfo method = typeof(Context).GetMethod(nameof(Context.GiveFood));
MethodInfo generic = method.MakeGenericMethod(animal.GetType());
generic.Invoke(context, new object[] { animal });
}
现在typeof(T)是Cat类,在GiveFood中,我可以获得与Cat类相关的方法。是否有另一种方法(不使用反射)来解决此问题?
答案 0 :(得分:0)
如果您使用直接强制转换而不是同时使用is
运算符和if
语句怎么办
try
{
context.GiveFood((Cat)animal);
}
catch(InvalidCastException ex) { }
答案 1 :(得分:0)
如果要在实现IAnimal
的任何对象上调用该方法,但希望将对象的实际类型用作方法GiveFood
的泛型参数,则反射方法可能是侵入性最低的方法
visitor pattern是一种类型安全但更复杂的方法,您可能要看一下(又称为双重调度,请参见下面的代码)。如果您希望GiveFood
方法对IAnimal
的某些特定类型的实现具有不同的实现,这将特别合适。
这涉及到定义一个新接口,
public interface IAnimalFeeder // the visitor
{
void GiveFood(IAnimal animal); // main entry point
// The generic implementation:
void GiveFood<TAnimal>(TAnimal animal) where TAnimal : IAnimal;
// + some specific implementations (if needed)
void GiveFood(Cat cat);
void GiveFood(Dog dog);
void GiveFood(Fish fish);
void GiveFood(Mouse mouse);
}
public class AnimalFeeder : IAnimalFeeder
{
public void GiveFood(IAnimal animal)
{
animal.Accept(this);
}
public void GiveFood<TAnimal>(TAnimal animal) where TAnimal : IAnimal
{
// here the type parameter TAnimal will be the actual implementation
// generic implementation
}
public void GiveFood(Cat cat)
{
// specific implementation
}
// etc...
}
public interface IAnimal
{
void Accept(IAnimalFeeder feeder);
}
public class Dog : IAnimal
{
public void Accept(IAnimalFeeder feeder) // same code for each implementation of IAnimal
{
feeder.GiveFood(this);
}
}
答案 2 :(得分:0)
您一般解决此问题的方法是在要公开的界面中包含足够的操作 来实现该方法。
因此,您的饲养员需要知道什么以喂养动物,体重是多少,是否是素食者,食肉动物,杂食动物,每天需要喂养多少次,或者您需要其他什么条件?了解他们。这些信息都需要放在您的IAnimal
界面上,并且每只动物都需要提供该信息的实现。
答案 3 :(得分:0)
一个简单易用的解决方案可以是使用dynamic:
Context context = new Context();
var cat = context.GetWhoLivesInZoo("Cat");
if (cat is IAnimal animal)
{
context.GiveFood((dynamic)animal);
}
但是请注意,dynamic
在内部使用了Reflection(通过缓存使其性能更高)。因此,如果您真的想避免反射,那么另一个答案中描述的“访问者模式”可能就是解决方法。