好的,所以我正在使用一组我无法控制的类,这些类在使用这些对象的一些非常通用的函数中。而不是写几十个基本上为每个类做同样事情的函数,而是决定使用泛型函数。
现在我正在处理的类有点奇怪,因为派生类共享许多相同的属性,但它们派生自的基类却没有。一个这样的属性示例是.Parent,它存在于大量派生类中,但不存在于基类上,我需要使用此属性。
为了便于理解,我创建了一个小例子如下:
class StandardBaseClass {} // These are simulating the SMO objects
class StandardDerivedClass : StandardBaseClass {
public object Parent { get; set; }
}
static class Extensions
{
public static object GetParent(this StandardDerivedClass sdc) {
return sdc.Parent;
}
public static object GetParent(this StandardBaseClass sbc)
{
throw new NotImplementedException("StandardBaseClass does not contain a property Parent");
}
// This is the Generic function I'm trying to write and need the Parent property.
public static void DoSomething<T>(T foo) where T : StandardBaseClass
{
object Parent = ((T)foo).GetParent();
}
}
在上面的示例中,调用DoSomething()将在基类的GetParent()实现中抛出NotImplemented Exception,即使我强制转换为T是StandardDerivedClass。
这与其他转换行为相反,其中转发会强制使用基类的实现。
我将此行为视为错误。有没有其他人遇到过这个?
答案 0 :(得分:4)
我将此行为视为错误。
这种行为是正确的。由于您的方法DoSomething
限制T
到StandardBaseClass
,因此您只能访问StandardBaseClass
的特定方法,而不能访问派生类的任何方法或属性。由于StandardBaseClass
没有Parent
属性,因此无效,并且设计无效。
这里有两个可能的选项 - 您可以使用反射来提取Parent属性,或使用C#4的动态类型,并将其视为动态对象。但是,两者都绕过编译器中的标准类型检查,因此需要在运行时进行额外的类型检查以验证Parent属性是否存在。
答案 1 :(得分:1)
创建包含Parent属性的接口。让每个具有Parent属性的类实现相互作用。然后,您将能够创建一个接受IHaveParent
类型参数的通用方法,它将做正确的事情。
答案 2 :(得分:1)
对于任何有兴趣的人,Stephen Cleary在msdn上回答了这种情况的简洁回答:
答案 3 :(得分:0)
对我来说,这是班级等级的分歧。这就是说,我的意思是基类具有父类,或者带有父类的派生类是从基类的抽象子类派生的。
Lol John说,接口而不是抽象类也足够了。
答案 4 :(得分:0)
你的想法是行不通的,因为编译器永远不能保证基类实际上会有这样的属性。并且它不仅仅根据它是否具有它来选择“正确的”。
您可以执行此操作的唯一方法是使用反射,然后在运行时测试所请求的属性是否存在于被检查类上。你必须判断自己,如果这是一个可行的方式为你的项目做(反思很慢,需要最大的权利)。
答案 5 :(得分:0)
这是正确的,因为编译器只知道它可以作为StandardBaseClass
绑定到您的类型。绑定是不在运行时完成(它可能决定使用StandardDerivedClass
重载。
如果您知道它是StandardDerivedClass
,那么为什么不直接投射呢?
object Parent = ((StandardDerivedClass)foo).Parent;
答案 6 :(得分:0)
这有点难看,但是您可以使用注册系统完成此操作,您可以在其中为不同的可能派生类注册委托,这些类公开“共享”属性/方法,然后使用类似Dictionary<Type,Func<SomeT>>
的内容来存储与会代表。如果您提前知道所有派生类型并且不必加载插件等,那么您也可以使用经典的丑陋if / else-if结构。无论哪种方式,您基本上都可以创建自己的替代虚拟方法表应该支持的内容。