Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Proposal: Stronger type Inference for generics calls #151

Closed
iam3yal opened this issue Feb 19, 2017 · 15 comments
Closed

Proposal: Stronger type Inference for generics calls #151

iam3yal opened this issue Feb 19, 2017 · 15 comments

Comments

@iam3yal
Copy link
Contributor

iam3yal commented Feb 19, 2017

Problem:

C# can infer the type from the context for types like this:

var x = 1; // int
var y = "one"; // string
var z = 1f; // float

Now, when it comes to generics, say we have the following class:

class Foo<T>
{
    public Foo(T value)
    {
    }
}

When we are creating Foo we must satisfy the generic argument and provide the type explicitly like this:

var x = new Foo<int>(1);
var y = new Foo<string>("one");
var z = new Foo<float>(1);

Solution:

I propose to have stronger type inference for generics calls this means you no longer need to satisfy the type argument manually because it would be inferred by the compiler:

Example 1:

var x = new Foo(1); // int
var y = new Foo("one"); // string
var z = new Foo(1f); // float

Example 2:

Foo(new Bar(1, "one")); // Bar<T, U>

Known Issues:

  1. When it comes to overload resolution in cases like this:

    class Foo<T>
    {
        public Foo(T value)
        {
        }
    
        public Foo(object value)
        {
        }
    
        public Foo(int value)
        {
        }
    }
    

    I thought that it should pick the generic constructor over anything else but as noted by @HaloFour in my previous proposal it's a breaking change so for the sake of compatibility it would have to pick the non-generic version.

  2. Another thing is sometimes we may need to disambiguate so in this case like @aluanhaddad I think that we should satisfy some or all of the generic type arguments to the point that it's no longer ambiguous.

Related Topics:

@jnm2
Copy link
Contributor

jnm2 commented Feb 19, 2017

Yes, and if classes Foo and Foo<> both exist, you should have to disambiguate the arity: new Foo<>(1f).

@Richiban
Copy link

Another thing that I've always found mildly irritating is that if any one of the type arguments cannot be inferred by the compiler then you must manually supply all type arguments.

Example:

void MyFunc<T1>(T1 item) => ...

// I can call the above function without supplying the type argument:
MyFunc("Hello world");

However, if I make no other change than to simply add another type argument:

void MyFunc<T1, T2>(T1 item) => ...

// I must now supply all type arguments because T2 cannot be inferred by the compiler:
MyFunc<string, object>("Hello world");

Now that C# will introduce some kind of wildcard into the language, can we use that to do F#-style partial type inference?

void MyFunc<T1, T2>(T1 item) => ...

// I can now supply *some* of the type arguments
MyFunc<_, object>("Hello world");

@jnm2
Copy link
Contributor

jnm2 commented Apr 21, 2017

@Richiban I'd prefer this progression: MyFunc<,>, MyFunc<, object>, MyFunc<string, object>

@DavidArno
Copy link

Worth checking out #395, which is related.

@jnm2,
I think I'd like to see:

MyFunc() // T1 & T2 can be inferred, as current
MyFunc<object>() // T1 can be inferred, T2 = object
MyFunc<string,>() // T1 = string; T2 can be inferred
MyFunc<string, object>() // T1 and T2 both explicitly specified

@jnm2
Copy link
Contributor

jnm2 commented Apr 21, 2017

Ah, yes definitely! Although you'd still need <,> syntax for disambiguation in some cases.

@DavidArno
Copy link

DavidArno commented Apr 21, 2017

You are likely right. I've only ever used the <,> syntax to use reflection with generic types. I don't really understand that syntax (I just copy/pasted/modified the code from Stack Overflow 😊), nor do I know what other uses it has.

Edit it occurs to me that "I've only ever used the <,> syntax to use reflection with generic types" is exactly the sort of thing you are talking about with "you'd still need <,> syntax for disambiguation in some cases." Doh!

@HaloFour
Copy link
Contributor

Reminds me of the "diamond" syntax in Java which is used when the generic type argument may be inferred:

// T is inferred to be Integer as Java generic inference is based on assignment/use
List<Integer> numbers = new ArrayList<>();

However, C# already uses similar syntax to denote open generic types, e.g. typeof(Dictionary<,>), so I think that I'd prefer something a little more explicit like what @Richiban suggested, using the "discard/wildcard identifier" in place of a generic type argument:

MyFunc<_, object>();
MyFunc<string, _>();

@jnm2
Copy link
Contributor

jnm2 commented Apr 21, 2017

@HaloFour Places where open generic types may be specified and places where generic types may be inferred are mutually exclusive. I really don't like reading or writing the extra underscore and space.

@HaloFour
Copy link
Contributor

HaloFour commented Apr 21, 2017

@jnm2

Yes, but in my opinion the meaning of the syntax really shouldn't change based on where it's used. And that space is optional. 😁

@jnm2
Copy link
Contributor

jnm2 commented Apr 21, 2017

@HaloFour So you want the meaning of syntax like MyFunc<string,> to be reserved for partially open partially closed types? Seems like such a waste to me.

@HaloFour
Copy link
Contributor

@jnm2

Sure. Could help with reflection tests like foo.GetType().ImplementsInterface(typeof(Dictionary<string,>)) which currently isn't possible now.

@munael
Copy link

munael commented Mar 12, 2019

Still needed. Nice QoL enhancement. What's blocking/controversial about it?

@juliusfriedman
Copy link

Quick example which illustrates an example IRL which causes issues:

`class Program
{

    internal class ObjectResult
    {
        public bool Error { get; set; }
        public string Message { get; set; }
    }

    internal class ObjectResult<T> : ObjectResult
    {
        public ObjectResult(T result)
        {
            Result = result;
        }
        public T Result { get; set; }
    }


    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
        var test = new ObjectResult() { Error = false };
        var works = new ObjectResult<int>(0) { Error = false };
        var anotherTest = new ObjectResult(0) { Error = false };
    }
}` 

Which can be worked around by

` internal class ObjectResult
{
public bool Error { get; set; }
public string Message { get; set; }
public Object BoxedValue { get; set; }
public ObjectResult(object result = null)
{
BoxedValue = result;
}
}

    internal class ObjectResult<T> : ObjectResult
    {
        public ObjectResult(T result = default(T)) : base(result)
        {
            Result = result;
        }
        public T Result { get; set; }
    }`

@jnm2
Copy link
Contributor

jnm2 commented Jul 14, 2019

Interesting, everyone's is different. Here's mine: https://gist.github.com/jnm2/a0031a31d04d6ed4d44d8dfc9265b8cf

@kblok
Copy link
Contributor

kblok commented Apr 6, 2021

It would also be awesome to infer generics from the initializers.

public class GenericClass<T>
{
	public T Key { get; set; }	
}

public class Program
{
	public static void Main()
	{
		var child = new GenericClass { Key = "foo"};
		Console.WriteLine(secure.Key);
	}
}

@333fred 333fred closed this as completed Apr 6, 2021
@dotnet dotnet locked and limited conversation to collaborators Apr 6, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests