简体   繁体   English

推断C#通用类型的子类

[英]Inferring C# Generic Type of Subclass

I have a generic class Proxy<T> , and I want to write another generic class with its type parameter being a Proxy. 我有一个泛型类Proxy<T> ,我想写另一个泛型类,其类型参数是一个代理。

I want to write: 我想写:

public class MyClass<U> where U : Proxy<T>

but the compiler reports The type or namespace name T could not be found . 但编译器报告The type or namespace name T could not be found

A solution I've found is to declare it like this: 我发现的一个解决方案是声明它是这样的:

public class MyClass<U, T> where U : Proxy<T>

but this seems clumsy as the client will have to declare two type parameters, like this: 但这似乎很笨拙,因为客户端必须声明两个类型参数,如下所示:

public class SomeClass { ... }
public class SomeProxy : Proxy<SomeClass> { ... }

and then in a client somewhere: 然后在某个客户端:

var proxyWrapper = new MyClass<SomeProxy, SomeClass>();

How can I do this without having to have two generic types on MyClass . 如何在MyClass上不必拥有两个泛型类型的情况下执行此操作。 After all, if we know the first is SomeProxy , it should follow that the second is SomeClass . 毕竟,如果我们知道第一个是SomeProxy ,那么应该遵循第二个是SomeClass

Maybe something like this would do the job, too? 也许这样的事情也可以做到这一点?

class Test<T> {
    public Test(Proxy<T> proxy) { this.MyProxy = proxy; }
    public Proxy<T> MyProxy { get; private set; }
}

You can have an interface IMyClass<SomeProxy> and a factory method that creates and returns an instance of MyClass<SomeProxy, SomeClass> . 您可以拥有一个接口IMyClass<SomeProxy>和一个工厂方法,该方法创建并返回MyClass<SomeProxy, SomeClass>的实例。 You may need to create the instance using Reflection. 您可能需要使用Reflection创建实例。

I have a code example here of a similar situation: the end user only cares about a single type parameter, but the implementation needs to have two. 我在这里有类似情况的代码示例:最终用户只关心单个类型参数,但实现需要有两个。 In my example, I don't have to use Reflection to create the instance, but it sounds like you may need to. 在我的示例中,我不必使用Reflection来创建实例,但听起来您可能需要这样做。

What you're trying to do is possible using compile-time constructs such as C++ templates, but not run-time constructs such as C# generics. 您正在尝试使用C ++模板等编译时构造,但不能使用C#泛型等运行时构造。

抱歉,如果两种类型都没有MyClass泛型,则无法在C#中执行此操作(除非您希望使用反射来创建它的实例。)

If you want T to remain generic in Myclass , then the MyClass instance still needs to resolve all internally used generic types and you HAVE TO declare it somewhere. 如果你希望T在Myclass保持通用,那么MyClass实例仍然需要解析所有内部使用的泛型类型,你必须在某处声明它。 The way to go is the verbose way you mentioned: 要走的路是你提到的冗长方式:

public class MyClass<U, T> where U : Proxy<T>

If you don't care about the generic type T in MyClass then create interface and use it instead: 如果你不关心MyClass的泛型类型T ,那么创建接口并使用它代替:

public interface IProxy { ... }
public class SomeClass { ... }
public class SomeProxy : Proxy<SomeClass>, IProxy { ... }
public class MyClass<U> where U : IProxy

and then in a client somewhere: 然后在某个客户端:

var proxyWrapper = new MyClass<SomeProxy>();

But do note that you cannot use type T in your interface declaration and Type U is now more general then before. 但请注意,您不能在接口声明中使用类型T,而类型U现在比以前更通用。

It turns out that all of the SomeProxy classes I want to deal with actually just override one method of Proxy<T> which has the signature: 事实证明,我想要处理的所有SomeProxy类实际上只是覆盖了具有签名的Proxy<T>一个方法:

 T LoadInternal(Identifier id)

So, what I've done is created an internal class inside MyClass which takes a Func<Identifier, T> in its constructor. 所以,我所做的是在MyClass中创建一个内部类,它在构造函数中使用了Func<Identifier, T> I can then pass a Func<Identifier, T> as a parameter to the constructor of MyClass and use my subclass in place of SomeProxy. 然后,我可以将Func<Identifier, T>作为参数传递给MyClass的构造函数,并使用我的子类代替SomeProxy。

Seems a bit convoluted, but it works for me. 看起来有点复杂,但它对我有用。 To summarise, I now have: 总结一下,我现在有:

public class MyClass<T>{
    private SomeProxy theProxy;

    public MyClass(Func<Identifier, T> loadDelegate){
        theProxy = new SomeProxy(loadDelegate);
    }

    /* Other methods here */

    class SomeProxy : Proxy<T>{
        private Func<Identifier, T> m_loadInternal;

        public SomeProxy(Func<Identifier, T> loadInternal){
            m_loadInternal = loadInternal;
        }

        protected override T LoadInternal(Identifier id){
            return m_loadInternal(id);
        }
    }
}

So, from client code, instead of writing a class which extends Proxy and then overriding LoadInternal in that class, I just create MyClass using: 因此,从客户端代码,而不是编写一个扩展代理,然后覆盖该类中的LoadInternal的类,我只是创建MyClass使用:

var myClass = new MyClass<T>(x => CodeWhichReturnsT());

How can I do this without having to have two generic types on MyClass. 如何在MyClass上不必拥有两个泛型类型的情况下执行此操作。 After all, if we know the first is SomeProxy, it should follow that the second is SomeClass. 毕竟,如果我们知道第一个是SomeProxy,那么应该遵循第二个是SomeClass。

Although you seem to have found an answer to the main part of the question, I figured I'd offer my understanding about this part. 虽然你似乎找到了问题主要部分的答案,但我想我会提供我对这部分的理解。 It sounds like you wish you could do something like this: 听起来你希望你能做到这样的事情:

class Proxy<T> 
{ 
    T Value { get; set; }
}
class MyClass<U> where U : Proxy<> { }

and have the compiler fill in the Proxy type parameter when you provide U . 并在提供U时让编译器填写Proxy类型参数。 Since you have declared U as inheriting from Proxy, you must intend to use one of the methods on Proxy, that probably use the T parameter, like so: 由于您已声明U继承自Proxy,您必须打算使用Proxy上的一个方法,这可能使用T参数,如下所示:

class MyClass<U> where U : Proxy<>
{
    void SomeMethod(U parameter)
    {
        var local = parameter.Value;
        //more code here...
    }
}

Now, what is the compiler supposed to infer for local here? 现在,编译器应该在什么local推断local This is the main problem I see that makes such a feature, if possible, hard to implement. 这是我看到的主要问题,如果可能的话,很难实现这样的功能。 If you don't want to use any methods that use the generic type of Proxy, you could instead make a non-generic base class and use that for U and sidestep the entire problem. 如果您不想使用任何使用泛型类型的代理的方法,您可以改为创建非泛型基类并将其用于U并避开整个问题。

I am not a compiler writer, but a couple possibilities of how this could be dealt with come to mind. 我不是编译器编写者,但想到如何解决这个问题的几种可能性。 It could just say object (or whatever other restriction you put on the type parameter in Proxy), but that doesn't seem quite right or quite what normal generics seem to do. 它可以说对象(或者你在代理中对类型参数施加的任何其他限制),但这似乎不太正确或者正常的泛型似乎做的事情。 This would also require the CLR to allow open generic types as a constraint on the generic parameter, which I doubt it does. 这也需要CLR允许打开泛型类型作为泛型参数的约束,我怀疑它是否存在。 The other option I could see is for the type to actually have the second type parameter, and the compiler to give you syntactic sugar to make it easier. 我能看到的另一个选项是类型实际上具有第二个类型参数,并且编译器为您提供语法糖以使其更容易。

Any way you go, this feature seems like a lot of work for a little benefit in what is probably a rare scenario, thus not likely to make the cut to get implemented. 无论你走到哪里,这个功能似乎都可以在很少的情况下获得一些好处,因此不太可能实现切割。

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

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