C# 使用具有不同约束的泛型类

C# 使用具有不同约束的泛型类,c#,generics,C#,Generics,我有两个传统的C#用户控件需要一起工作。 我有一个现有的对话框,需要在其中添加一个现有的通用usercontrol 我尝试了下面的层次结构示例 interface Foo<T> {} interface Bar<T> { T DataObject { get; set; } } public class ClassA<T> where T : Foo<T> { public ClassA(T dataObject) {

我有两个传统的C#用户控件需要一起工作。 我有一个现有的对话框,需要在其中添加一个现有的通用usercontrol

我尝试了下面的层次结构示例

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
在特定的实例中是
ClassC
-那么在代码中有没有一种方法可以使用
T
创建
ClassB
的实例-在这种情况下
ClassA
中的
T
符合
ClassB
的约束,因为
T
ClassC

我还没有弄明白怎么做,或者如果可能的话——我倾向于相信我做不到


正如我在上面所写的,我有一个基于反射的解决方案,我只是不喜欢使用反射和只获得运行时验证。但是在这种情况下,对于需要一起工作的两个遗留对象,我可能已经没有选择了

首先,你的类型有点奇怪。它们是递归的,
ClassB
需要一个
T
来实现
Bar
,它的结构与
ClassB
相同。也许您想用
ClassB
来实现
Bar
,而不是要求它作为类型参数


无论如何,你不能这样做。为了能够编写
ClassB
,编译器需要确保
T
在运行时是
ClassB
的有效类型参数。只有当
T
ClassA
上的类型参数至少与
ClassB
中的类型参数一样严格时,才会出现这种情况

不幸的是,即使是一个可以确保这种情况的硬类型检查也不允许您编写
ClassB

因此,如果不能编写
ClassB
,就无法在编译时获得静态类型安全性。因此,即使您创建了
ClassB
(您可以)的实例,您也无法访问该实例上的
DataProperty
,因为您无法将其转换为
ClassB

因此,为了解决这个问题,您要么只能使用反射来访问
DataProperty
,要么调用
ClassA
中具有类型约束的方法。我将向您展示这两种解决方案:

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;
    }
}
公共类ClassA
T:Foo在哪里
{
公共类A(T数据对象)
{
if(typeof(Bar).IsAssignableFrom(typeof(T)))
{
//方法1,调用泛型函数
MethodInfo mi=typeof(ClassA).GetMethod(“SetBDataObject”).MakeGenericMethod(typeof(Bar));
调用(这个,新对象[]{dataObject});
//方法2,通过反射来完成这一切
Type Type=typeof(ClassB)。MakeGenericType(typeof(T));
对象x=Activator.CreateInstance(类型);
type.GetProperty(“DataObject”).SetValue(x,DataObject);
}
}
公共对象数据对象(TB obj)
TB:Bar在哪里
{
var x=新类b();
x、 DataObject=obj;
返回x;
}
}

代码中可能会混淆的第一件事是,您在两个类
ClassA
ClassB
中使用了相同的字母
T
作为类型参数

首先,我将陈述显而易见的:
当您调用
var x=new ClassB()时
此处
T
的约束在
ClassA
的上下文中(即
T:Foo
),而
newclassb()
期望
T
T:Bar
的约束匹配

在我看来,您遇到的问题的根本原因是设计问题。看起来您有点混淆了
类型

让我们走一遍:
从:

对象的类定义了对象的实现方式 定义对象的内部状态及其实现 行动

相反,对象的类型仅指其接口-集合 它可以响应的请求的数量

一个对象可以有多种类型,不同类的对象可以有多种类型 同样的类型

在代码中使用接口意味着针对
类型进行编码(这很好!)。
检查
if(dataObject是Bar)
并在结果构建
ClassB
where
!typeof(U).Equals(typeof(T)
表示严重依赖于实现(例如

如果你问我,我认为你应该尝试以下方法之一:

  • 使用用于构造
    ClassB
    。在专用工厂中,您可以添加更多的逻辑和验证,以决定如何构造它(根据您的代码,由于类型不匹配,因此不清楚…)

  • 如果可能,解决
    Foo
    Foo
    之间的关系,并在接口中声明约束。在这种情况下,两个接口应具有相同的约束fto
    T


  • 您的代码将不会编译-
    ClassB
    不包含
    DataObject
    的定义。这可能是可能的,但与您现在的构造不同。DataObject是
    Bar
    上的属性,而不是
    ClassB
    ,您的if语句可能应该包含
    if(DataObject是Bar){var dataObjectBarT=dataObject as Bar;
    这将使您的dataObject成为一个
    Bar
    ,但不清楚您接下来要寻找的是什么。代码经过编辑,因此B显式定义了dataObject属性。我有一个实现了Foo和Bar的ClassC在C#中如何具有多态性?Foo和Bar是接口您的观察是某种程度上是正确的。由于我使用的是两个遗留组件,它们的类与类型焦点有所不同。我无法轻松修复ad.2,因为这是遗留组件,两种类型都需要相当多的时间来重新构造。ad.1.实际上是一点o
    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;
        }
    }