简体   繁体   English

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

[英]Working with Generic classes with different constraints

I have two legacy C# user controls I need to work together. 我有两个需要一起使用的旧C#用户控件。 I have an existing dialog onto which I need to add an existing generic usercontrol. 我有一个现有对话框,需要在其中添加现有的通用usercontrol。

I have tried to sample the hierarchy below 我试图对下面的层次结构进行采样

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; }
}

The existing dialog, ClassA currently don't have any generic contraints, but could easily be changed to require T implementing Foo<T> . 现有对话框ClassA当前没有任何通用约束,但是可以很容易地更改为要求T实现Foo<T> The userControl, ClassB is based on another interface, Bar<T> . userControl ClassB基于另一个接口Bar<T> In practice, Bar<T> objects always implements Foo<T> - in theory of course not. 实际上, Bar<T>对象始终实现Foo<T> -理论上当然不是。

Are there any construction I can use to declare an object of type ClassB , and get compile time validation? 我可以使用任何构造来声明ClassB类型的对象并获得编译时验证吗?

The construction above will give me a compile error stating: 上面的构造将给我一个编译错误,指出:

The type 'T' cannot be used as type parameter 'T' in the generic type of method ClassB<T> . 在方法ClassB<T>的通用类型中,类型'T'不能用作类型参数'T'。 There is no implicit reference conversion from 'T' for Bar<T> 对于Bar<T>没有从'T'的隐式引用转换。

I can make the ClassB object with Reflection, setting the properties using Reflection as well - but I would prefer a compile time solution. 我可以使用Reflection创建ClassB对象,也可以使用Reflection设置属性-但我更喜欢编译时解决方案。

But in my current situation with two existing dialogs - i'm not sure I am able to. 但是在当前情况下,有两个现有对话框-我不确定我是否能够。

Any help is appreciated - also just if it is stating what I expect, that it can't be done. 感谢您提供任何帮助-即使只是说明了我的期望,也无法做到。

--EDIT - 编辑

Trying to elaborate a bit. 试图详细说明。 The problem rises when I have a ClassC that implements both Foo<T> and Bar<T> 当我具有同时实现Foo<T>Bar<T>ClassC时,问题就会出现

public class ClassC<T> : Foo<T>, Bar<T>
{
   T DataProperty
}

If I make an instance of ClassA<ClassC> , that is T in the specific instance is ClassC - then is there a way in code I can use T in creating an instance of ClassB - in this case T in ClassA does live up to the ClassB constraints, since T is ClassC . 如果我创建ClassA<ClassC>的实例,则在特定实例中的TClassC那么在代码中有没有一种方法可以使用T创建ClassB的实例-在这种情况下, ClassA中的T确实符合由于TClassC ,因此具有ClassB约束。

I havent figured out how or if possible - tend to believe I can't. 我还没有想出如何或可能的话-倾向于相信我做不到。

As I wrote above, I have a solution based on reflection, i'm just not fan of using reflection and getting run-time validation only. 就像我上面写的,我有一个基于反射的解决方案,我只是不喜欢使用反射和仅获得运行时验证。 But in this case with two legacy objects that need to work together I might be running out of options. 但是在这种情况下,有两个需要协同工作的旧式对象,我可能会用光所有选项。

First of all, your types are a bit weird. 首先,您的类型有些奇怪。 They are kind-of recursive, with ClassB<T> requiring a T that implements Bar<T> which has the same structure as ClassB<T> . 它们是一种-的递归的,与ClassB<T>需要T实现Bar<T>具有相同的结构ClassB<T> Maybe you meant to ClassB<T> to implement Bar<T> instead of requiring it as a type parameter? 也许您打算让ClassB<T> 实现 Bar<T>而不是要求它作为类型参数?


Anyway you cannot do this. 无论如何,您不能这样做。 In order to be able to write ClassB<T> , the compiler needs to ensure that T is a valid type parameter for ClassB<> at runtime . 为了能够编写ClassB<T> ,编译器需要在运行时确保TClassB<>的有效类型参数。 This can only be the case when the type parameters on ClassA<T> for T are at least as restrictive as the ones from ClassB<T> . 仅当ClassA<T>T的类型参数至少与ClassB<T>的类型参数一样严格时,才可能出现这种情况。

Unfortunately, even a hard type check which would ensure that this is the case will not allow you to write ClassB<T> . 不幸的是,即使可以确保确实如此的硬类型检查也不允许您编写ClassB<T>

So without being able to write ClassB<T> , you will not be able to get static type safety at compile-time. 因此,如果不能编写ClassB<T> ,则在编译时将无法获得静态类型的安全性。 So even when you create an instance of ClassB<T> (which you can), you won't be able to access DataProperty on it since you will not be able to cast it as a ClassB<T> . 因此,即使创建了ClassB<T>的实例(可以),您也将无法访问它的DataProperty ,因为您无法将其ClassB<T>ClassB<T>

So in order to solve this, you would either have to access DataProperty using reflection only, or call a method inside ClassA<T> that does have the type constraint. 因此,为了解决此问题,您要么必须仅使用反射来访问DataProperty ,要么必须在ClassA<T>中调用确实具有类型约束的方法。 I'll show you both solutions: 我将向您展示两种解决方案:

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;
    }
}

The first thing that probably is confusing in your code, is that you have used the same letter T as the Type parameter in both classes ClassA<T> and ClassB<T> . 您的代码中可能造成混乱的第一件事是,您在两个类ClassA<T>ClassB<T>都使用了与Type参数相同的字母T

I'll start by stating the obvious: 我将首先说明显而易见的内容:
when you call var x = new ClassB<T>(); 当您调用var x = new ClassB<T>(); the constraint for T here is in the context of ClassA<T> (ie T : Foo<T> ), while new ClassB<T>() expects T to match the constraint of T : Bar<T> . 这里的T约束是在ClassA<T>的上下文中(即T : Foo<T> ),而new ClassB<T>()期望T匹配T : Bar<T>的约束。

It seems to me that underlying reason for the issue you are having is a design problem. 在我看来,您遇到的问题的根本原因是设计问题。 It looks like you a little mix up between types and classes . 看起来您在typesclasses之间有点混淆。

Lets walk it through: 让我们逐步通过:
from the Gang of Four Design Patterns book : 选自“ 四种设计模式”一书

An objects's class defines how the object is implemented .The class defines object's internal state and the implementation of its operations. 对象的定义对象的实现方式。该类定义对象的内部状态及其操作的实现。

In contrast, an objects's type only refers to its interface -the set of requests to which it can respond. 相反,对象的类型仅指其接口-它可以响应的请求集。

An object can have many type, and object of different classes can have the same type. 一个对象可以具有多种类型,而不同类的对象可以具有相同的类型。

The usage of interfaces in your code implies coding against types (that's good!). 在代码中使用接口意味着针对types编码(这很好!)。
Checking for if (dataObject is Bar<T>) and upon the result constructing a ClassB<U> where !typeof(U).Equals(typeof(T) implies heavily relying on implementation (eg class ). 检查if (dataObject is Bar<T>)并根据结果构造ClassB<U> ,其中!typeof(U).Equals(typeof(T)表示严重依赖实现(例如class )。

If you ask me, I think you should try one of the following: 如果您问我,我想您应该尝试以下方法之一:

  1. Use the factory pattern for constructing ClassB . 使用工厂模式构造ClassB In the dedicated factory you can add some more logics and verifications in order to decide how to construct it (from your code, it is not clear since the types do not match...). 在专用工厂中,您可以添加更多逻辑和验证来决定如何构造它(从您的代码中,由于类型不匹配,因此尚不清楚。)。

  2. If possible, resolve the relation between Foo<T> and Foo<T> and declare the constraints in the interfaces. 如果可能,请解析Foo<T>Foo<T>之间的关系,并在接口中声明约束。 In that case, both interfaces should have same constraints fto T 在这种情况下,两个接口应该有FTO相同的约束T

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM