Вопрос

In JavaScript, it's not uncommon to see self-invoking functions:

var i = (function(x) {
    return x;
})(42);

// i == 42

While I'm certainly not comparing the languages, I figured such a construct would be translatable to C#, provided a language version with support:

var i = (delegate(int x) {
    return x;
})(42);

Or:

var i = ((x) => {
    return x;
})(42);

Or even:

var i = (x => x)(42);

However, every version is in error:

Method name expected

Are self-invoking anonymous methods unsupported (due either to explicit forbiddance, or the impossibility of type inference), or is there an error in my attempts?

I'm venturing a guess that because there was no method declaration (Func<T,T>) against which the types could be inferred, it can't sort out the implied types, figures I meant to call a declared method by name, and really goofed up the syntax.


Errata

Before this question is flooded with:

var i = new Func<int, int>(x => x)(42);

I should say that I was hoping to leverage type inference, but I suppose that may not be possible due to being overly-implicit.

So, clarifying the question a bit; we know we can var i = new Func<int, int>(x => x)(42); but without creating an instance of, or casting to Func, is this possible?

Use-Case

For those curious (or concerned) the use case was something like this:

var o = new {
    PropertyA = () => {
        // value computation
    }(),
    PropertyB = () => {
        // value computation
    }(),
};
Это было полезно?

Решение 2

I suppose someone from the C# team can give a much better answer but I try my shot. Question here is not if it's a best practice or not but if it's possible and, if not, why.

Let's start with what you would write:

var i = (x => x)(42);

Pretty straightforward, you pass an integer (42) to a lambda, it plays with it and it returns the same value. Its parameter is an integer (inferred from its usage), it returns an integer (inferred from the expression) and i is another integer (inferred from return value).

Unfortunately this is broken in the very first step. Let me cite big Jon:

Compiler tries to work out the types of the parameters for lambda expressions based on the context in which it's used.

What does it mean? Actually that the compiler need some information but it can use only:

  • An explicit cast, constructor or declaration like in (Func<int, int>)(x => x)) or new Func<int, int>(x => x). Required types here are given or inferred using the required final type.
  • An assignment (again because the final type can be inferred) like in: Func<int, int> f = (x => x);

In your case the compiler have to infer function type from its parameters and it's not possible because parameters are validated against the expression and not vice-versa.

Why this is not possible in C#? Because (x => x) is just a delegate then it can be any delegate that accept an integer parameter (if we suppose the compiler can infer lhs from rhs and validate rhs according to lhs) and returns another integer. Actually conversion between delegates are not allowed in C# so the expression is invalid (even if this special delegate won't be assigned to any variable and it won't be used in the future). It could be Func<int, int> or Expression<Func<int, int>> or any other delegate (for bool parameter it may even bee Predicate<bool>).

Of course this is a C# design decision, the same expression - for example - in VB.NET is perfectly valid:

 Dim i = (Function(x) x)(42)

Different language, different rules to obey, C# goal is to avoid this kind of ambiguity.

Другие советы

var x = ((Func<int, int>)(y => y * 2))(10);

The problem is that when the compiler sees y => y * 2 it classifies it as an Expression instead of a Func by default, unless there is some contextual information to let it know it should be a Func. By casting it to Func we give it the context it needs.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top