Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow type parameters to be declared co/contra variant on classes. #171

Closed
mburbea opened this issue Jan 30, 2015 · 7 comments
Closed

Allow type parameters to be declared co/contra variant on classes. #171

mburbea opened this issue Jan 30, 2015 · 7 comments

Comments

@mburbea
Copy link

mburbea commented Jan 30, 2015

Currently type parameters can be declared co/contra variant on either an interface or a delegate. I propose adding support for classes.

The rules would remain the same, any in T cannot appear as any method return type nor on gettable properties. Any out T cannot appear as a parameter or on settable properties. My main motivation for this is really around things like Task<T> where I sometimes want to collect Task<Dog>, Task<Cat>, Task<Fish> into a collection of IEnumerable<Task<Pet>> and whenAlll/whenAny and only inspect their pet based result.

It cannot be legal on structs as the assignment would be a copy operation.

@svick
Copy link
Contributor

svick commented Jan 30, 2015

How would this work with accessibility? For example, could out T type have a non-public field of type T (just like Task<T> does currently)?

If that were allowed, how would you make sure the following couldn't happen?

class Task<out T>
{
    private T value;

    public static void SetValue(Task<T> task, T value)
    {
        task.value = value;
    }
}Task<Pet>.SetValue(new Task<Dog>(), new Cat());

If it wasn't allowed, then I think that significantly limits the usefulness of the feature. For example, I don't know how would you implement Task<T> under such restrictions.

@mburbea
Copy link
Author

mburbea commented Jan 30, 2015

Presumably it would be legal to declare private fields of co/contra variant generics. The static method you described could not be declared as T is an out parameter and therefore can not appear as an input.

@thomaslevesque
Copy link
Member

I'm not sure it would be feasible... there were good reasons to limit variance to interfaces and delegates in C# 4 (although I admit I don't remember exactly what they were). I agree that the Task<T> issue is annoying, but a better solution for this would have been to make Task<T> an interface rather than a class... well, I guess it's too late for that now (or is it?)

@mburbea
Copy link
Author

mburbea commented Feb 7, 2015

Yeah if TPL used an ITask<T> this probably wouldn't have been such a large issue. I'd be curious as to what insight let to to limiting variance to just delegates and intefaces.

@thomaslevesque
Copy link
Member

@mburbea see this answer by Eric Lippert: http://stackoverflow.com/a/2734070/98713

@iskiselev
Copy link

@svick and @mburbea , so if I understand correct, Task can be implemented with something like:

class Task<out T>
{
    private readonly TaskResultAccessor<T> resultAccessor;

    private T Result { return resultAccessor.Value;}
}

class TaskResultAccessor<T> 
{
    internal T value;

    public static void SetValue(TaskResultAccessor<T> accesor, T value)
    {
        accesor.value = value;
    }
}

So we split Task and result storage into to classes: immutable covarint Task and non-variant result storage.
But it gives us not much more than using interfaces for it.

@gafter
Copy link
Member

gafter commented Aug 15, 2016

This would be a large undertaking including required CLR support, for marginal gain in language expressiveness. We don't think this is where we want to invest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants