开发者

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.

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.

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. After all, if we know the first is SomeProxy, it should foll开发者_开发知识库ow that the second is 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; }
}


Sorry, you just can't do this in C# without having MyClass generic on both types (unless you want to use reflection to create instances of it.)


You can have an interface IMyClass<SomeProxy> and a factory method that creates and returns an instance of MyClass<SomeProxy, SomeClass>. You may need to create the instance using 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.

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.


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. 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:

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.


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:

 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. I can then pass a Func<Identifier, T> as a parameter to the constructor of MyClass and use my subclass in place of 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:

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


How can I do this without having to have two generic types on MyClass. After all, if we know the first is SomeProxy, it should follow that the second is 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. 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:

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? 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.

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. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜