4

In Unity, here's a category in c#,

public static class HandyExtensions
{
public static IEnumerator Tweeng( this System.Action<float> v, float d )
    {
    while (..)
        {
        ..
        v( 13f*t );
        yield return null;
        }
    v(13f);
    }

Compiles fine!

But if you try to use it,

yield return StartCoroutine(
            ( (x)=>laser=x ).Tweeng(3.141f)
        );

this saddening error appears:

Assets/scripts/...cs(116,34): error CS0023: The .' operator cannot be applied to operand of typeanonymous method'

I have tears about this.

How could c# let us down?

Surely there's a way to call "on" a lambda like that, for an extension?

BTW the workaround is to go 3.14f.Tweeng((x)=>laser=x) but it's not as cool.

smdrager
  • 7,327
  • 6
  • 39
  • 49
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 10
    Tears? Saddening? Can you tone down the drama in this question a little? – rory.ap Jan 29 '16 at 14:48
  • 4
    whoa - we have about the same points! AND math minor. Turn on humour-detector bud :) – Fattie Jan 29 '16 at 14:50
  • 3
    You probably should have realized by now that no question you ask on this site *belongs to you*. They belong to the community. Questions should be professional and clear and without additional "fluff" that distracts from them. If someone has a similar question and googles it, they're going to be distracted or confused when they see your title. It's unnecessary. – rory.ap Jan 29 '16 at 14:52
  • 1
    You're not even a non-native english speaker, there's no excuse :) Anyway this is a serious question, where are the serious comments?? – Fattie Jan 29 '16 at 14:54
  • 2
    Please don't name things `var`. C# has been carefully designed so that it is legal to do so, so that legacy code is not made illegal. But it is so potentially confusing with the declaration syntax of an implicitly typed local that it is a bad practice to do so in new code. – Eric Lippert Jan 29 '16 at 14:58
  • 1
    I also note that you appear to be using yield to implement coroutines. Why not use await instead? The semantics of an async method are logically much more like a coroutine than the semantics of an iterator. – Eric Lippert Jan 29 '16 at 15:00
  • (Sorry, I just accidentally typed var) You know, in Unity3D I'm afraid I've never seen or considered using "await" - I don't know about it. In the frame based Unity game engine, `yield`ing brings you to the next frame. (Thus, a line of code inside Unity's `Update()` is identical to a `yield`ed line of code in a coroutine in Unity.) I'm afraid I only use your awesome language in the context of that game engine; I've never seen `await` used in a `MonoBehaviour` I don't know if that's a deep idiom or if there's a technical reason. Thanks for that pointer, I'll look in to it! – Fattie Jan 29 '16 at 15:18
  • I'm pretty sure await doesn't work within Unity3D – Daniel Slater Jan 29 '16 at 15:39
  • Ah, I see. Unity was invented before await was added to the language, and so they are probably using yield as poor-mans-coroutines out of convenience. Had it been designed today they would likely use await. Behind the scenes, yield and await generate very similar code. – Eric Lippert Jan 29 '16 at 15:53
  • That's utterly fascinating, Eric! thx. I'm calling Copenhagen :) I'd love to have more time to use c# generally outside of Unity. – Fattie Jan 29 '16 at 15:59

2 Answers2

15

I'm sorry this saddens you, but this choice was made deliberately by the language design team. The code which evaluates whether a given extension method is valid requires that the receiver have a clear type, and lambda expressions do not have a type.

There was some debate on this point, but ultimately it was decided that (1) the proposed feature is potentially confusing or error-prone if typeless expressions like lambdas, method groups and null literals get to be receivers of extension methods, and (2) the proposed feature is not at all necessary to make LINQ work. We were very constrained in our schedules when implementing C# 3 and anything that was not necessary to make LINQ work was cut. It was much easier to design, implement and test the feature of "don't allow lambdas as receivers" than to have to consider all the potentially odd cases where a lambda, method group or null was being used as the receiver.

As others have said, you can simply cast the lambda, or put it in a variable and then use the variable as the receiver.

Alternatively, as you note, you could consider using the float as the receiver in your specific example.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Makes perfect sense now that you explain it. * awe * Thanks! – Fattie Jan 29 '16 at 14:57
  • I'd say that was a good call about "potentially confusing or error-prone". If that was strewn all over the place, it could be a mess. – krillgar Jan 29 '16 at 14:57
  • To be sure, if you do cast or `new` the lambda, *it is very beautiful indeed*. Sending in lambdas to extensions makes for beautiful, beautiful, code on the page in games. Most languages, very much c#, have an inherent emergent physical beauty on the page if used strongly. – Fattie Jan 29 '16 at 15:41
  • Don't mean to bother you @EricLippert .. it just [occurred to me](http://stackoverflow.com/questions/35580895/properties-in-c-sharp-in-unity-unity5?noredirect=1#35580977) that you can't hide the backing variable of a Property in current c#. As a language design *dilletente* this interests me; wouldn't that be a high-priority item? Of course, I may be completely confused - it goes without saying. – Fattie Feb 23 '16 at 15:27
  • @JoeBlow: I'm not sure exactly what you're getting at, but it might have something to do with this: https://blogs.msdn.microsoft.com/ericlippert/2009/01/14/automatic-vs-explicit-properties/ – Eric Lippert Feb 23 '16 at 16:04
8

Quell your tears fair Joe, let not despair drive you from your dream! If you explicitly cast it, it should work. Try:

yield return StartCoroutine(
        ((System.Action<float>)( (x)=>laser=x )).Tweeng(3.141f)
    );
Daniel Slater
  • 4,123
  • 4
  • 28
  • 39