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

Proposal: Instance Method Delegate (Method References) #5444

Closed
alrz opened this issue Sep 25, 2015 · 19 comments
Closed

Proposal: Instance Method Delegate (Method References) #5444

alrz opened this issue Sep 25, 2015 · 19 comments

Comments

@alrz
Copy link
Member

alrz commented Sep 25, 2015

Currently you can create delegates from static methods and non-static methods of a specific instance:

Action f = Console.WriteLine;
Action g = foo.InstanceMethod;

But what if you want to create a direct delegate from a non-static method without a specific instance? Currently this is supported by Delegate.CreateDelegate but it's too verbose as you must specify the exact delegate type and pass the MethodInfo using reflection. I propose the instance method delegates (aka method references in Java):

Action<Foo> h = Foo::InstanceMethod;

The syntax used here should be able to expose method accessors for properties:

// without an instance
Func<Foo, int> getter = Foo::Property;
Action<Foo, int> setter = Foo::Property;
// with an instance
Func<int> getter = foo::Property;
Action<int> setter = foo::Property;

This can also cover the #3561:

var query = db.Orders
    .Select(Order::Employee)
    .OrderBy(Employee::Name);

Note that these members are properties rather than methods but since they are internally implemented as methods so it makes sense to create delegates from them. For the fact that each property may consist of two methods (getter and setter) the right choice should be inferred from usage.

In these specific cases where the target type can be inferred, an alternative would be .Select(::Employee) — a syntax closer to what have proposed in the aforementioned issue.

EDIT: there should be a special translation for LINQ, so:

from item in db.Employees
orderby item.Name
select item;
// would translate to
db.Employees.OrderBy(::Name);

to avoid creating double-invocation delegates.

@RichiCoder1
Copy link

That's actually rather clever.

@HaloFour
Copy link

👍

@tpetrina
Copy link

👍

@aluanhaddad
Copy link

👍

@bbarry
Copy link

bbarry commented Oct 14, 2015

Delegate.CreateDelegate is the method you are looking for

edit: (there is no method Delegate.CreateInstance)

Action<Foo> h = Foo::InstanceMethod;

could be currently written as this:

Action<Foo> h = (Action<Foo>)Delegate.CreateDelegate(
    typeof(Action<Foo>),
    null,
    typeof(Foo).GetMethod("InstanceMethod", new Type[0]
    );

or:

Action<Foo> h = h1 => h1.InstanceMethod();

But the latter can also be written as an expression tree:

Expression<Action<Foo>> h = h1 => h1.InstanceMethod();

So:

var query = db.Orders
    .Select(Order::Employee)
    .OrderBy(Employee::Name);

those are likely expressions, not "Instance Method Delegates"

@alrz
Copy link
Member Author

alrz commented Oct 14, 2015

@bbarry Fixed.

@bbarry
Copy link

bbarry commented Oct 14, 2015

I've edited the previous post; I am not sure what I didn't read?

For the fact that each property may consist of two methods (getter and setter) the right choice should be inferred from usage.

When would referencing a setter this way be applicable?

The current proposal is ambiguous in some cases, would:

Func<string, string> trim = string::Trim;

represent:

Func<string, string> trim = s => s.Trim();

or

Func<string, string> trim = s => s.Trim(new char[0]);

What about if none of the method overloads have 0 parameters:
Func<List, int> findIndex = List::FindIndex;

@HaloFour
Copy link

@bbarry I don't see how that case is ambiguous. Why would the compiler even consider the second string.Trim overload? C#'s overload resolution has always been fine with choosing between Method() and Method(params T[]) and this would be no different. If the delegate definition was Func<string, char[], string> then it would favor the second overload.

Also, your second example would simply be a compiler error since there is no List<int>::FindIndex overload that accepts a List<int> and returns an int. Valid delegate signatures for List<int>::FindIndex would be Func<List<int>, Predicate<int>, int>, Func<List<int>, int, Predicate<int>> or Func<List<int>, int, int, Predicate<int>> (or other delegates with compatible signatures), everything else would be a compile-time error.

The one way it could be ambiguous is if a class has an instance method and a static method of the same name where the static method accepts an instance of the class as the first parameter, assuming that the :: (or whatever) notation would support both static and instance members. If it does then that would result in a compiler error, just as it does in Java.

@bbarry
Copy link

bbarry commented Oct 14, 2015

yeah I meant var findIndex = ... but thinking about what I've already said, that would be ambiguous already because List<int>::FindIndex might be an expression or a delegate.

@thomaslevesque
Copy link
Member

This is a great idea. It could probably be implemented using open-instance delegates.

@alrz
Copy link
Member Author

alrz commented Dec 2, 2015

Beside of event subscribers/unsubscribers (#6129) and ctors (#140), it'd be great to be able to expose operator delegates with C++ syntax, e.g.

Func<int, int, int> add = int::operator+;
list.Aggregate(int::operator+);

or simply

list.Aggregate(::operator+);

when compiler can infer the types.

@Unknown6656
Copy link

If possible I would also like to see something like this working:

MethodInfo nfo = (MyApplication::Programm::Main).GetMethodInfo();

or alternatively using the typeof-keyword:

 MethodInfo nfo = typeof(MyApplication::Programm::Main);

This is rather important to me as I am using reflection very often.

@svick
Copy link
Contributor

svick commented Feb 7, 2016

@Unknown6656 That's a separate feature, see #1653.

@omariom
Copy link

omariom commented Feb 8, 2016

Great idea

@jeske
Copy link

jeske commented Jul 1, 2016

I'd prefer a type-safe reflection mechanism to get unbound field and property references, but since that seems unlikely, this is an interesting alternative.

I like the proposal, but not expecting the compiler to resolve the ambiguity between get and set through context. I'd prefer to see a syntax, more like:

// without an instance
Func<Foo, int> getter = Foo::Property;
Action<Foo, int> setter = Foo:=Property;
// in context
SomeMethodTakingFunc(::Property);
SomeMethodTakingAction(:=Property);

@miniBill
Copy link

miniBill commented Sep 17, 2016

With regards to syntax:
How about Class.Method or Class.Property.Accessory? After all that's what we already use for static members. Using the same syntax seems sane.

With regards to properties:
How about simply Class.Property.get and Class.Property.set? They would have no ambiguity and they would be readable even by someone knowing nothing about this feature.

@aluanhaddad
Copy link

@miniBill that's a sensible idea but there's no ambiguity that needs to be eliminated because the signatures of a getter and the setter are sufficiently distinct.

@miniBill
Copy link

miniBill commented Oct 5, 2016

@aluanhaddad That's true, but for T Property { get; set; }, Class.Property has type T, whereas one could write var getter = Property.get and getter would have type Func<Class, T>.

@alrz alrz closed this as completed Mar 21, 2017
@alrz
Copy link
Member Author

alrz commented Mar 21, 2017

Moved to dotnet/csharplang#84

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