3 added 233 characters in body
source | link

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<FooAndBarRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).

You could also hack together some sort of inference logic using reflection, but I don't think this is a good idea. Not that it won't work, but it just complicates everything and you still can't cover every fringe case anyway.


Some answers here have suggested using a generic method parameter next to a generic class parameter, but I strongly disagree here. The additional generic parameter you wish to identify (Post) is based on the class definition (PostRequest : Request<Post>) and is not freely choosable by whoever calls the method.

For example, you wouldn't want this to be possible:

var postRequestPerformer = new Performer<PostRequest>();

Post post = postRequestPerformer.Perform<Post>(); // this is okay
Foo foo = postRequestPerformer.Perform<Foo>(); // this is not okay

The second call should not be allowed because PostRequest does not actually implement Request<Foo>. But because you're using a separate class generic parameter and method generic parameter, it's still possible that after instantiating your object (thereby setting the class generic parameter to a fixed type) to perform different method calls with different generic method parameters.

The only way to prevent this is to define both generic parameters on the same level, which is what you already discovered. This can be done on the method level:

public TModel Perform<TRequest, TModel>(Action<TRequest> action) where TRequest : Request<TModel> where TModel : class

or on the class level:

public class Performer<TRequest, TModel> where TRequest : Request<TModel> where TModel : class
{
    public TModel Perform(Action<TRequest> action)
    {
        // ...
    }
}

The main point is that these two generic parameters are intended to be decided at the same time (since one cannot change without the other then also changing), which means they have to be defined on the same scope.

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<FooAndBarRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).


Some answers here have suggested using a generic method parameter next to a generic class parameter, but I strongly disagree here. The additional generic parameter you wish to identify (Post) is based on the class definition (PostRequest : Request<Post>) and is not freely choosable by whoever calls the method.

For example, you wouldn't want this to be possible:

var postRequestPerformer = new Performer<PostRequest>();

Post post = postRequestPerformer.Perform<Post>(); // this is okay
Foo foo = postRequestPerformer.Perform<Foo>(); // this is not okay

The second call should not be allowed because PostRequest does not actually implement Request<Foo>. But because you're using a separate class generic parameter and method generic parameter, it's still possible that after instantiating your object (thereby setting the class generic parameter to a fixed type) to perform different method calls with different generic method parameters.

The only way to prevent this is to define both generic parameters on the same level, which is what you already discovered. This can be done on the method level:

public TModel Perform<TRequest, TModel>(Action<TRequest> action) where TRequest : Request<TModel> where TModel : class

or on the class level:

public class Performer<TRequest, TModel> where TRequest : Request<TModel> where TModel : class
{
    public TModel Perform(Action<TRequest> action)
    {
        // ...
    }
}

The main point is that these two generic parameters are intended to be decided at the same time (since one cannot change without the other then also changing), which means they have to be defined on the same scope.

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<FooAndBarRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).

You could also hack together some sort of inference logic using reflection, but I don't think this is a good idea. Not that it won't work, but it just complicates everything and you still can't cover every fringe case anyway.


Some answers here have suggested using a generic method parameter next to a generic class parameter, but I strongly disagree here. The additional generic parameter you wish to identify (Post) is based on the class definition (PostRequest : Request<Post>) and is not freely choosable by whoever calls the method.

For example, you wouldn't want this to be possible:

var postRequestPerformer = new Performer<PostRequest>();

Post post = postRequestPerformer.Perform<Post>(); // this is okay
Foo foo = postRequestPerformer.Perform<Foo>(); // this is not okay

The second call should not be allowed because PostRequest does not actually implement Request<Foo>. But because you're using a separate class generic parameter and method generic parameter, it's still possible that after instantiating your object (thereby setting the class generic parameter to a fixed type) to perform different method calls with different generic method parameters.

The only way to prevent this is to define both generic parameters on the same level, which is what you already discovered. This can be done on the method level:

public TModel Perform<TRequest, TModel>(Action<TRequest> action) where TRequest : Request<TModel> where TModel : class

or on the class level:

public class Performer<TRequest, TModel> where TRequest : Request<TModel> where TModel : class
{
    public TModel Perform(Action<TRequest> action)
    {
        // ...
    }
}

The main point is that these two generic parameters are intended to be decided at the same time (since one cannot change without the other then also changing), which means they have to be defined on the same scope.

2 added 1652 characters in body
source | link

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<PostRequest>Perform<FooAndBarRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).


Some answers here have suggested using a generic method parameter next to a generic class parameter, but I strongly disagree here. The additional generic parameter you wish to identify (Post) is based on the class definition (PostRequest : Request<Post>) and is not freely choosable by whoever calls the method.

For example, you wouldn't want this to be possible:

var postRequestPerformer = new Performer<PostRequest>();

Post post = postRequestPerformer.Perform<Post>(); // this is okay
Foo foo = postRequestPerformer.Perform<Foo>(); // this is not okay

The second call should not be allowed because PostRequest does not actually implement Request<Foo>. But because you're using a separate class generic parameter and method generic parameter, it's still possible that after instantiating your object (thereby setting the class generic parameter to a fixed type) to perform different method calls with different generic method parameters.

The only way to prevent this is to define both generic parameters on the same level, which is what you already discovered. This can be done on the method level:

public TModel Perform<TRequest, TModel>(Action<TRequest> action) where TRequest : Request<TModel> where TModel : class

or on the class level:

public class Performer<TRequest, TModel> where TRequest : Request<TModel> where TModel : class
{
    public TModel Perform(Action<TRequest> action)
    {
        // ...
    }
}

The main point is that these two generic parameters are intended to be decided at the same time (since one cannot change without the other then also changing), which means they have to be defined on the same scope.

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<PostRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<FooAndBarRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).


Some answers here have suggested using a generic method parameter next to a generic class parameter, but I strongly disagree here. The additional generic parameter you wish to identify (Post) is based on the class definition (PostRequest : Request<Post>) and is not freely choosable by whoever calls the method.

For example, you wouldn't want this to be possible:

var postRequestPerformer = new Performer<PostRequest>();

Post post = postRequestPerformer.Perform<Post>(); // this is okay
Foo foo = postRequestPerformer.Perform<Foo>(); // this is not okay

The second call should not be allowed because PostRequest does not actually implement Request<Foo>. But because you're using a separate class generic parameter and method generic parameter, it's still possible that after instantiating your object (thereby setting the class generic parameter to a fixed type) to perform different method calls with different generic method parameters.

The only way to prevent this is to define both generic parameters on the same level, which is what you already discovered. This can be done on the method level:

public TModel Perform<TRequest, TModel>(Action<TRequest> action) where TRequest : Request<TModel> where TModel : class

or on the class level:

public class Performer<TRequest, TModel> where TRequest : Request<TModel> where TModel : class
{
    public TModel Perform(Action<TRequest> action)
    {
        // ...
    }
}

The main point is that these two generic parameters are intended to be decided at the same time (since one cannot change without the other then also changing), which means they have to be defined on the same scope.

1
source | link

You cannot do this, because you cannot guarantee that there is only one generic type that could possibly be inferred.

Well, to be fair, you can guarantee that when your deriving from a class, but you cannot do that when dealing with interface, because a class can implement multiple interfaces.

public FooAndBarRequest: IRequest<Foo>, IRequest<Bar> { /* ... */ }

Now let's look again at your intended usage:

var result = client.Perform<PostRequest>(x => x.Id = 1);

So, should var result be a Foo or a Bar?

There's no way for the compiler to infer whether you're expecting a Foo or a Bar, and therefore the compiler deems your code ambiguous (and not logically consistent) and therefore rejects it.


You could argue that this could be inferred for inheritance. But it wouldn't work for interfaces. And that would mean that the compiler would have to treat generic classes and generic interfaces differently, which is going to considerably complicate everything (both for the developers who have to create the compiler and for the developers who have to work with the compiler and understand what it expects).