我有两个遗留的C#用户控件,我需要一起工作。 我有一个现有的对话框,我需要添加一个现有的通用用户控件。
我试图对下面的层次结构进行抽样
interface Foo<T> {}
interface Bar<T>
{
T DataObject { get; set; }
}
public class ClassA<T> where T : Foo<T>
{
public ClassA(T dataObject)
{
//Do stuff if T implements Bar<T> - Pseudocode ahead
if(var T is Bar<T>)
{
var x = new ClassB<T>();
//x is typesafe, and I can set DataObject
x.DataObject = dataObject;
}
}
}
public class ClassB<T> where T : Bar<T>
{
T DataObject { get; set; }
}
现有对话框ClassA
目前没有任何通用约束,但可以轻松更改为要求T实施Foo<T>
。
userControl ClassB
基于另一个界面Bar<T>
。在实践中,Bar<T>
个对象总是实现Foo<T>
- 理论当然不是。
我是否可以使用任何构造来声明ClassB
类型的对象,并获得编译时验证?
上面的结构会给我一个编译错误说明:
类型&#39; T&#39;不能用作类型参数&#39; T&#39;在泛型中 方法
ClassB<T>
。没有隐式引用转换 &#39; T&#39;Bar<T>
我可以使用Reflection创建ClassB
对象,也可以使用Reflection设置属性 - 但我更喜欢编译时解决方案。
但是在我目前的情况下有两个现有的对话框 - 我不确定我能够做到。
任何帮助都会受到赞赏 - 也就是说它能说明我的期望,它无法完成。
- 编辑
试着详细说明一下。
当我有ClassC
同时实现Foo<T>
和Bar<T>
时,问题就会出现
public class ClassC<T> : Foo<T>, Bar<T>
{
T DataProperty
}
如果我创建ClassA<ClassC>
的实例,那个特定实例中的T
是ClassC
- 那么我可以在代码中使用T
来创建ClassB
的一个实例 - 在这种情况下,T
中的ClassA
确实符合ClassB
约束,因为T
是ClassC
。
我没有想出如何或如果可能 - 倾向于相信我不能。
正如我上面所写,我有一个基于反射的解决方案,我不喜欢使用反射并仅获得运行时验证。但在这种情况下,需要协同工作的两个遗留对象可能会耗尽选项。
答案 0 :(得分:0)
首先,你的类型有点奇怪。它们是递归的,ClassB<T>
要求T
实现Bar<T>
,其结构与ClassB<T>
相同。也许您打算ClassB<T>
实施 Bar<T>
而不是将其作为类型参数?
无论如何你不能这样做。为了能够编写ClassB<T>
,编译器需要确保T
是运行时ClassB<>
的有效类型参数。仅当ClassA<T>
T
上的类型参数至少与ClassB<T>
中的类型参数一样具有限制性时,才会出现这种情况。
不幸的是,即使是确保这种情况的硬类型检查也不允许您编写ClassB<T>
。
因此,无法编写ClassB<T>
,您将无法在编译时获得静态类型安全性。因此,即使您创建了ClassB<T>
的实例(您可以),您也无法访问DataProperty
,因为您无法将其转换为ClassB<T>
因此,为了解决这个问题,您可能必须仅使用反射访问DataProperty
,或者调用具有类型约束的ClassA<T>
内的方法。我会告诉你两个解决方案:
public class ClassA<T>
where T : Foo<T>
{
public ClassA(T dataObject)
{
if (typeof(Bar<T>).IsAssignableFrom(typeof(T)))
{
// method 1, calling a generic function
MethodInfo mi = typeof(ClassA<T>).GetMethod("SetBDataObject").MakeGenericMethod(typeof(Bar<T>));
mi.Invoke(this, new object[] { dataObject });
// method 2, doing it all with reflection
Type type = typeof(ClassB<>).MakeGenericType(typeof(T));
object x = Activator.CreateInstance(type);
type.GetProperty("DataObject").SetValue(x, dataObject);
}
}
public object SetBDataObject<TB> (TB obj)
where TB : Bar<TB>
{
var x = new ClassB<TB>();
x.DataObject = obj;
return x;
}
}
答案 1 :(得分:0)
您的代码中可能令人困惑的第一件事是,您在T
和ClassA<T>
类中使用了与ClassB<T>
相同的字母{。}}。
我首先要说明显而易见的事:
当您致电var x = new ClassB<T>();
时T
的约束位于ClassA<T>
(即T : Foo<T>
)的上下文中,而new ClassB<T>()
期望T
匹配T : Bar<T>
的约束。
在我看来,您遇到问题的根本原因是设计问题。看起来您在types
和classes
之间有点混淆。
让我们一起来看看:
来自Gang of Four Design Patterns book:
对象的类定义了对象的实现方式。该类 定义对象的内部状态及其实现 操作
相反,对象的类型仅指其界面 - 集合 可以回复的请求。
对象可以有多种类型,不同类的对象可以有 相同的类型。
代码中接口的使用意味着针对types
进行编码(这很好!)
检查if (dataObject is Bar<T>)
并根据结果构建ClassB<U>
,其中!typeof(U).Equals(typeof(T)
意味着严重依赖于实施(例如class
)。
如果你问我,我认为你应该尝试下列之一:
使用factory pattern构建ClassB
。在专用工厂中,您可以添加一些逻辑和验证,以便决定构建它的 (从您的代码中,由于类型不匹配,因此不清楚)。
如果可能,解析Foo<T>
和Foo<T>
之间的关系并声明接口中的约束。在这种情况下,两个接口应具有相同的约束fto T