3

Consider the following function:

public void DoSomething<TSource>(TSource data)
{
   // ...
}

In C#, the compiler may implicitly infer TSource's type by inspecting the method's argument:

DoSomething("Hello") // Works fine. DoSomething<string>("Hello") is called.

Why can't we do the same when there is a generic return value?

For instance:

public TResult DoSomething<TResult, TSource>(TSource data)
{
    // ...
}

TResult cannot be inferred (and I understand why), but the compiler can surely infer TSource's type, can't it?.

However, this does not compile:

int result = DoSomething<int>("Hello"); // This should call DoSomething<int,string>("Hello")
Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • That's just the way `C#` is. Either you give it all the generic arguments, or you give it none as long as they can be inferred. Can't go half-way. Not sure of the technical reason for this though. – Rob Dec 11 '15 at 17:18
  • You may check this one out: [Is there a reasonable approach to “default” type parameters in C# Generics?](http://stackoverflow.com/questions/707780/is-there-a-reasonable-approach-to-default-type-parameters-in-c-sharp-generics) – Daniel Dec 11 '15 at 17:31

1 Answers1

5

It's not a matter of compiler - it's that C# requires that you either explicitly specify all the type arguments or let it infer all of them.

There is no middle ground using the syntax you've attempted, and I imagine it's because if you had a generic method like this:

public void DoSomething<T1, T2>(T1 data, T2 data)
{
    // ...
}

And you used it like this:

var obj1 = "Hello!";
var obj2 = "Hello?";
DoSomething<IEnumerable<char>>(obj1, obj2);

The last line could be shorthand for two equally valid things:

DoSomething<string, IEnumerable<char>>(obj1, obj2);
DoSomething<IEnumerable<char>, string>(obj1, obj2);

A different syntax (like <string, ?>) or additional inference rules would have to be put in place to make cases like this meaningful and non-ambiguous. I imagine the design team thought that it wasn't worth it.


Note that if you really want partial generic type inference, there is a common pattern of splitting the call into two calls, with a helper object to hold the information between the calls. This is essentially currying, applied to type parameters.

I'll present the pattern in a form that uses a public interface and an private implementation but if you're not concerned about that, you can skip the interface altogether.

public TResult DoSomething<TResult, TSource>(TSource data)
{
    // ...
}

Would become:

public IWrapper<TSource> DoSomething<TSource>(TSource data)
{
    return new WrapperImplementation<TSource>(data);
}

Where:

public interface IWrapper<T>
{
    TResult Apply<TResult>();
}

class WrapperImplementation<T> : IWrapper<T>
{
    private readonly T _source;

    public WrapperImplementation(T source)
    {
        _source = source;
    } 

    public TResult Apply<TResult>()
    {
        // ...
    }
}

The usage is:

DoSomething("Hello").Apply<int>();
Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104