我有一个通用的方法:
public bool DoSomething<T>(T item) where T: IBase
{
return DoSomethingSpecific(item);
}
public bool DoSomethingSpecific(IBase item)
{
return true;
}
public bool DoSomethingSpecific(IAdvanced item)
{
return false;
}
请注意,IAdvanced接口从IBase接口派生/继承。
我发现如果我调用DoSomething,其中项目的类型为IAdvanced,它仍然总是返回false。我不明白这一点。我知道,因为IAdvanced是一个类型IBase(因为它是这个接口的子代),它可能会导致DoSomethingSpecific方法的2个重载类型之间的混淆。但是,正如我对有限的C#知识所理解的那样,应该在这里选择IAdvanced方法。这是我如何得出这个结论的一个例子:
public class Advanced: IAdvanced
{
public void CallMethod()
{
DoSomething(this);
}
}
这会产生真正的价值。
但是,如果我这样做:
public class Advanced: IAdvanced
{
public void CallMethod()
{
DoSomethingSpecific(this);
}
}
它返回false,这是我所期望的。
我不得不说我之前从未使用过仿制药。我曾尝试过,但总是陷入这样的情况,然后完全没有看到使用泛型的意义(除了树和链表之类的数据结构)。
这次我决定来这里寻求一些建议。我试图做的是否有明显的问题?尝试做我在这里忙的事情可能没有意义吗?
答案 0 :(得分:4)
我无法重现这一点,这意味着可能正在发生其他事情。我怀疑你实际上意味着说它总是返回 true 。这就是我在这里看到的:
using System;
public interface IBase {}
public interface IAdvanced : IBase {}
public class Base : IBase {}
public class Advanced : IAdvanced {}
class Test
{
public static bool DoSomething<T>(T item) where T: IBase
{
return DoSomethingSpecific(item);
}
public static bool DoSomethingSpecific(IBase item)
{
return true;
}
public static bool DoSomethingSpecific(IAdvanced item)
{
return false;
}
static void Main()
{
Console.WriteLine(DoSomething(new Base())); // True
Console.WriteLine(DoSomething(new Advanced())); // True
}
}
现在你也写道:
我知道由于IAdvanced是一个IBase类型(因为它是这个接口的子代),它可能会导致DoSomethingSpecific方法的2个重载类型之间的混淆。但是,正如我对有限的C#知识所理解的那样,应该在这里选择IAdvanced方法。
当然,你应该期望 false
被退回 - 而我希望能够返回true
。
您可以看到,DoSomething<T>
内的重载决策是在编译该方法时确定的。该选择是一次 - 对于每个不同的T
都不是一次。因此,如果您查看已编译的IL DoSomething<T>
,则仅会看到对DoSomethingSpecific(IBase)
,从不 DoSomethingSpecific(IAdvanced)
的调用。这里没有多态性。
至于你如何“修复”这个问题 - 你需要更详细地解释你想要实现的目标。特别是,如果你有这个代码,你会想要发生什么:
IBase x = new AdvancedImplementation();
DoSomething(x);
此处T
为IBase
,但值将引用IAdvanced
的实现。如果你想在这种情况下执行DoSomethingSpecific(IAdvanced)
,如果你正在使用C#4,那么你可以使用动态类型:
public static bool DoSomething(IBase item)
{
dynamic dynamicItem = item;
// Dispatch dynamically based on execution-time type
return DoSomethingSpecific(dynamicItem);
}
请注意该方法不再需要通用 - 它不会带来任何好处。
另一方面,如果你想根据T
决定只调用哪一个,你需要使用类似ivowiblo的解决方案。
就个人而言,我会尝试以避免这两种解决方案 - 我通常会发现这种事情是一种设计的症状,可以通过其他方式得到改善。
答案 1 :(得分:2)
我会删除编译时检查并使其成为运行时:
public bool DoSomething(IBase item)
{
var itemAdvanced = item as IAdvanced;
if (itemAdvanced != null)
return DoSomethingSpecific(itemAdvanced);
else
return DoSomethingSpecific(item);
}
原因是,如果您的调用者只是静态地知道该对象为IBase
,但在运行时它是IAdvanced
,那么将其视为IAdvanced
不是首选}?这可能也是做你想做的最快的方式。如果您认为有必要,我只会采用dynamic
方法,例如因为可能有很多不同的方法。
答案 2 :(得分:1)
据他所知,这是一个IBase。编译器需要确定你调用哪种方法,这就是为什么它总是选择那个。
一个肮脏的伎俩就是这样做:
public static bool DoSomething<T>(T item) where T: IBase
{
var isAdvanced = typeof(IAdvanced).IsAssignableFrom(typeof(T));
return isAdvanced ? DoSomethingSpecific((IAdvanced)item) : DoSomethingSpecific(item);
}
另一种方法是使用Double-Dispatch / Visitor模式:
public interface IDoSomethingVisitor {
bool DoSomethingSpecific(IBase base);
bool DoSomethingSpecific(IAdvanced adv);
}
DoSomething方法将在您的IBase界面中:
public interface IBase{
void DoSomething(IDoSomethingVisitor visitor);
}
在您的实施中:
public class Base : IBase
{
public bool DoSomething(IDoSomethingVisitor visitor)
{
visitor.DoSomething(this);
}
}
public class Advanced : IAdvanced
{
public bool DoSomething(IDoSomethingVisitor visitor)
{
visitor.DoSomething(this);
}
}
在这种情况下,使用纯继承解决问题。实际的实例是解析调用哪个方法的实例。不,如果。