views:

76

answers:

4

Maybe I'm overworked, but this isn't compiling (CS0411). Why?

interface ISignatur<T>
{
    Type Type { get; }
}

interface IAccess<S, T> where S : ISignatur<T>
{
    S Signature { get; }    
    T Value { get; set; }
}

class Signatur : ISignatur<bool>
{
    public Type Type
    {
        get { return typeof(bool); }
    }
}

class ServiceGate
{
    public IAccess<S, T> Get<S, T>(S sig) where S : ISignatur<T>
    {
        throw new NotImplementedException();
    }
}

static class Test
{
    static void Main()
    {
        ServiceGate service = new ServiceGate();
        var access = service.Get(new Signatur()); // CS4011 error
    }
}

Anyone an idea why not? Or how to solve?

+2  A: 

Get<S, T> takes two type arguments. When you call service.Get(new Signatur()); how does the compiler know what T is? You'll have to pass it explicitly or change something else about your type hierarchies. Passing it explicitly would look like:

service.Get<Signatur, bool>(new Signatur());
Kirk Woll
this is the answer.
vulkanino
Yes, i wanted to avoid the explicite call. But if as from Eric Lippert explaint, the generic constraints are not used for solving the generic return types, then this would not work. Thank you!
Ben
A: 

Kirk's answer is right on. As a rule, you're not going to have any luck with type inference when your method signature has fewer types of parameters than it has generic type parameters.

In your particular case, it seems you could possibly move the T type parameter to the class level and then get type inference on your Get method:

class ServiceGate<T>
{
    public IAccess<S, T> Get<S>(S sig) where S : ISignatur<T>
    {
        throw new NotImplementedException();
    }
}

Then the code you posted with the CS0411 error could be rewritten as:

static void Main()
{
    // Notice: a bit more cumbersome to write here...
    ServiceGate<SomeType> service = new ServiceGate<SomeType>();

    // ...but at least you get type inference here.
    IAccess<Signatur, SomeType> access = service.Get(new Signatur());
}
Dan Tao
A: 

As I mentioned in my comment, I think the reason why this doesn't work is because the compiler can't infer types based on generic constraints.

Below is an alternative implementation that will compile. I've revised the IAccess interface to only have the T generic type parameter.

interface ISignatur<T>
{
    Type Type { get; }
}

interface IAccess<T>
{
    ISignatur<T> Signature { get; }
    T Value { get; set; }
}

class Signatur : ISignatur<bool>
{
    public Type Type
    {
        get { return typeof(bool); }
    }
}

class ServiceGate
{
    public IAccess<T> Get<T>(ISignatur<T> sig)
    {
        throw new NotImplementedException();
    }
}

static class Test
{
    static void Main()
    {
        ServiceGate service = new ServiceGate();
        var access = service.Get(new Signatur());
    }
}
Dr. Wily's Apprentice
A: 

Now my aim was to have one pair with an base type and a type definition (Requirement A). For the type definition I want to use inheritance (Requirement B). The use should be possible, without explicite knowledge over the base type (Requirement C).

After I know now that the gernic constraints are not used for solving the generic return type, I experimented a little bit:

Ok let's introducte Get2:

class ServiceGate
{
    public IAccess<C, T> Get1<C, T>(C control) where C : ISignatur<T>
    {
        throw new NotImplementedException();
    }

    public IAccess<ISignatur<T>, T> Get2<T>(ISignatur<T> control)
    {
        throw new NotImplementedException();
    }
}

class Test
{
    static void Main()
    {
        ServiceGate service = new ServiceGate();
        //var bla1 = service.Get1(new Signatur()); // CS0411
        var bla = service.Get2(new Signatur()); // Works
    }
}

Fine, but this solution reaches not requriement B.

Next try:

class ServiceGate
{
    public IAccess<C, T> Get3<C, T>(C control, ISignatur<T> iControl) where C : ISignatur<T>
    {
        throw new NotImplementedException();
    }

}

class Test
{
    static void Main()
    {
        ServiceGate service = new ServiceGate();
        //var bla1 = service.Get1(new Signatur()); // CS0411
        var bla = service.Get2(new Signatur()); // Works
        var c = new Signatur();
        var bla3 = service.Get3(c, c); // Works!! 
    }
}

Nice! Now the compiler can infer the generic return types. But i don't like it. Other try:

class IC<A, B>
{
    public IC(A a, B b)
    {
        Value1 = a;
        Value2 = b;
    }

    public A Value1 { get; set; }

    public B Value2 { get; set; }
}

class Signatur : ISignatur<bool>
{
    public string Test { get; set; }

    public IC<Signatur, ISignatur<bool>> Get()
    {
        return new IC<Signatur, ISignatur<bool>>(this, this);
    }
}

class ServiceGate
{
    public IAccess<C, T> Get4<C, T>(IC<C, ISignatur<T>> control) where C : ISignatur<T>
    {
        throw new NotImplementedException();
    }
}

class Test
{
    static void Main()
    {
        ServiceGate service = new ServiceGate();
        //var bla1 = service.Get1(new Signatur()); // CS0411
        var bla = service.Get2(new Signatur()); // Works
        var c = new Signatur();
        var bla3 = service.Get3(c, c); // Works!!
        var bla4 = service.Get4((new Signatur()).Get()); // Better...
    }
}

My final solution is to have something like ISignature<B, C>, where B ist the base type and C the definition...

Ben