Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile time generics syntax

Tags:

c#

generics

class GenericWrapper<T>
{   
}

class WrapperInstance : GenericWrapper<string>
{   
}

class Usage
{
    public static Usage Create<T1, T2> (T2 t2) where T1 : GenericWrapper<T2>
    {
        return null;
    }
}

...

// works
Usage.Create<WrapperInstance, string>("bar");

// doesnt work
Usage.Create<WrapperInstance>("bar");

I suspect the answer is no, but is there a way I can make the last line compile?

I want the compiler to force me to provide a string argument without having to know or first go and examine WrapperInstance to see what T of GenericWrapper it implements.

I know I can make it compile by either using the first method or by taking object as the argument and doing runtime checking, but thats not the question ;) I largely suspect these are my only two options.

Thanks

like image 579
Andrew Bullock Avatar asked Jul 16 '26 05:07

Andrew Bullock


2 Answers

I suspect the answer is no, but is there a way I can make the last line compile?

No. Create has two generic type parameters. You either specify none or you specify both. In the case of none, the compiler will try to infer the types from the invocation arguments. However, in this case it can not because T1 never appears in the argument list. Therefore you must specify both.

like image 193
jason Avatar answered Jul 17 '26 19:07

jason


There are two problems here:

  • You want to infer just one type argument, and specify the other. You can't do that with normal type inference. However, you could make Usage generic, thus specifying one type argument there, and letting the other be inferred using the generic method:

    Usage<WrapperInstance>.Create("foo");
    

    That's something I've often done before, but that just leads to the second problem...

  • The type parameter you want to specify (T1) is constrained by the one you want to infer (T2). The above example can't do that, as Usage<WrapperInstance> doesn't "have" a T2 to validate... and you can't constrain an existing type parameter on a generic method - only ones which are introduced in the method.

There's one way I think we could do this:

public class Usage
{
    public static Usage<T2> For<T2>(T2 t2)
    {
        return new Usage<T2>(t2);
    }
}

public class Usage<T2>
{
    private readonly T2 t2; // Assuming we need it

    public Usage(T2 t2)
    {
        this.t2 = t2;
    }

    // I don't know what return type you really want here
    public static Foo Create<T1>() where T1 : GenericWrapper<T2>
    {
        // Whatever
    }
}

You'd use it like this:

Usage.Foo("bar").Create<WrapperInstance>();

Without knowing more about what you're trying to do, I don't know whether or not that's helpful - but it does manage to accomplish what you were after in terms of:

  • Validating the WrapperInstance type argument
  • Inferring the string type argument
like image 23
Jon Skeet Avatar answered Jul 17 '26 17:07

Jon Skeet