In Foof We Trust: A Dialogue

In Foof We Trust: A Dialogue

Rate This
  • Comments 53

User: The typeof(T) operator in C# essentially means “compiler, generate some code that gives me an object at runtime which represents the type you associate with the name T”. It would be nice to have similar operators that could take names of, say, methods and give you other metadata objects, like method infos, property infos, and so on. This would be a more pleasant syntax than passing ugly strings and types and binding flags to various Reflection APIs to get that information.

Eric: I agree, that would be a lovely sugar. This idea has been coming up during the C# design meeting for almost a decade now. We call the proposed operator “infoof”, since it would return arbitrary metadata info. We whimsically insist that it be pronounced “in-foof”, not “info-of”.

User: So implement it already, if you’ve been getting requests for the better part of ten years! What are you waiting for?

Eric: First, as I’ve discussed before on my blog, we have a very limited time and money budget for design, implementation, testing, documentation and maintenance, so we want to make sure we’re spending it on the highest-value features. This one has never made it over that bar.

Second, the feature seems at first glance to be simple, but in fact has a rather large “design surface” and would require adding a fair amount of new syntax to the language.

Third, it is nothing that you cannot do already with existing code; it’s not necessary, it’s just nice.

All these are enough “points against” the feature that it’s never made it into positive territory. It will stay on the list for hypothetical future versions of the language, but I would not expect it to ever happen.

User: Can you elaborate on some of the problems?

Eric: Let’s start with a simple one. Suppose you have infoof(Bar) where Bar is an overloaded method. Suppose there is a specific Bar that you want to get info for. This is an overload resolution problem. How do you tell the overload resolution algorithm which one to pick? The existing overload resolution algorithm requires either argument expressions or, at the very least, argument types.

Expressions seem problematic. Either they are evaluated unnecessarily, perhaps producing unwanted side effects, or you have a bunch of expressions in code that looks like a function call but isn’t actually. Either way is potentially confusing. So we’d want to stick with types.

User: Well then, it ought to be straightforward then. infoof(Bar(int, int)), done.

Eric: I notice that you’ve just introduced new syntax; nowhere in C# previously did we have a parenthesized, comma-separated list of types. But that’s not at all hard to parse.

User: Exactly. So go with that.

Eric: OK smart guy, what about this strangely familiar case?

class C<T>
{
  public void Bar(int x, T y) {}
  public void Bar(T x, int y) {}
  public void Bar(int x, int y) {}
}
class D : C<int>
{ …

You have some code in D that says infoof(Bar(int, int)). There are three candidates. Which do you pick?

User: Well, which would overload resolution pick if you called Bar(10, 10) ? Overload resolution tiebreaker rules say to pick the non-generic version when faced with this awful situation.

Eric: Great. Except that’s not the one I wanted. I wanted the method info of the second version. If your proposed syntax picks one of them then you need to give me a syntax to pick the others.

User: Hmm. It's hard because overload resolution actually gives you no way to force the call to go to one of the two “generic param” methods in this bizarre case, so some mechanism stronger than overload resolution needs to be invented if you want the feature to allow getting info of any method.

But this is a deliberately contrived corner case; just cut it. You don’t have to support this scenario.

Eric: And now you start to see how this always goes in the design meeting! It is very easy to design a feature that hits what initially looks like 80% of the cases. And then you discover that the other 80% of the cases are not covered, and the feature either takes 160% of its budget, or you end up with a confusing, inconsistent and weak feature, or, worse, both.

And how do we know what scenarios to cut? We have to design a feature that does something in every possible case, even if that something is an error. So we have to at least spend the time to consider every possible case during the design. There are a lot of weird cases to consider!

User: What are some of the other weird cases?

Eric: Just off the top of my head, here are a few. (1) How do you unambiguously specify that you want a method info of an specific explicit interface implementation? (2) What if overload resolution would have skipped a particular method because it is not accessible? It is legal to get method infos of methods that are not accessible; metadata is always public even if it describes private details. Should we make it impossible to get private metadata, making the feature weak, or should we make it possible, and make infoof use a subtly different overload resolution algorithm than the rest of C#?  (3) How do you specify that you want the info of, say, an indexer setter, or a property getter, or an event handler adder?

There are lots more goofy edge cases.

User: I’m sure that we could come up with a syntax for all of those cases.

Eric: Sure. None of these problems are unsolvable. But they almost all require careful design and new syntax, and we would soon end up spending most of our design, implementation and testing budget on this trivial “sugar” feature, budget that we could be spending on more valuable features that solve real problems.

User: Well, how about you do the easy 80% and make “the other 80%” into error cases? Sure, the smaller feature is weaker and might be inconsistent, but something is better than nothing, and it’ll be cheaper to just do the easy ones.

Eric: That doesn’t actually make it cheaper a lot of the time. Any time we make a corner case into an error we need to carefully specify exactly what the error case is so that we can implement it correctly and test it confidently. Every error case still spends design, implementation and test budget that could have been spent elsewhere.

We would need to come up with a sensible error message, localize the error message into I don’t know how many languages, implement the code that detects the weird scenarios and gives the right error, test the error cases and document them. And we would need to deal with all the users who push back on each error case and request that it work for their specific scenario.

Trying to scope the feature down so that it is smaller adds work, and it is possible that it adds more work in the long run than simply making the larger version of the feature work in the first place. There are no cheap features.

User: Bummer. Because most of the time I really just want to get the method info of the method I’m currently running, for logging purposes during my test suites. It seems a shame to have to solve all these overload resolution problems just to get information about what’s happening at runtime.

Eric: Maybe you should have said that in the first place! The aptly named GetCurrentMethod method does that.

User: So what you’re telling me is no infoof?

Eric: It’s an awesome feature that pretty much everyone involved in the design process wishes we could do, but there are good practical reasons why we choose not to. If there comes a day when designing it and implementing it is the best way we could spend our limited budget, we’ll do it. Until then, use Reflection.

  • Given that the guidance around reflection has, since day one, been "[bold]don't use it[/bold] [fineprint]unless you really really have to[/fineprint]," it's not surprising that people jump at any possible alternative to it.  It's awkward to use, error prone, and "slow" (which I'd guess for 90%+ of the cases it's used isn't an issue, but we all have this burning need to to make code "fast").

    However, the .NET ecosystem has evolved quite a bit since 1.0, and we now live in a world of expression trees, dependency properties, code<->contract generation, ubiquitous unit tests, right-click refactoring, etc. etc. etc.  Every one of those tools has a single shared requirement: metadata.  Without it, they are useless, and as more and more tools leverage the power of metadata, it becomes more and more difficult to avoid reflection.

    In short, the old guidance is showing its age.  Sooner or later, metadata will have to become a first-class citizen, with easy access, compile-time sanity checks and a fast runtime path.  Infoof may or may not be the solution that does that, but it's hard to see the need diminishing.

  • Just before c# 3 was released I read an article about reflection and Lambda and combining the two to make type safe reflection:

    http://www.clariusconsulting.net/blogs/kzu/archive/2006/07/06/TypedReflection.aspx

    it might be intereseting to some of the followers of this post.

  • GetCurrentMethod is not a good solution.

    Method can be inlined and GetCurrentMethod will return the method that IL was inlined into.

    What's inlined can be changed between versions of call and so the only thing one can do is to add NoInlining attribute.

    It's ugly

  • > Method can be inlined and GetCurrentMethod will return the method that IL was inlined into.

    I'm not 100% certain on this, but I believe that calling GetCurrentMethod() will actually prevent the calling method from being inlined, specifically so that you don't get wrong results.

    GetCallingAssembly() is the one that can give you the wrong result because of inlining (and documented as such).

  • "Like I said before, how many free unicorns are you willing to give up in order to get this free pony? -- Eric"

    Haha, nice one Eric.  That made me laugh.  You have quite a way with words ;)  (That is so going on my quote list)

    As always I really appreciate the insight into the decision making process behind adding features.  Very nice article :)

  • I'm not sure why people see infoof as an 'alternative' to using reflection - given that we'd expect the resulting type of the expression to be a System.Reflection.MethodInfo, it's more just a better way in to reflection than starting off with a string.

    Basically, it comes down to the fact that typeof(Foo) turns into an IL ldtoken operation on the type metadata to get a runtime type handle, which is then resolved into a Type object by a call to Type.GetTypeFromHandle(), which is nice and quick. What we want is a clean way to compile code into the equivalent ldtoken to generate a RuntimeMethodHandle and have it resolved to a MethodInfo - something no library can accomplish, hence the pressure on the language and compiler team for a solution. That suggests a couple of things to me:

    1) it should never return a PropertyInfo, only a MethodInfo - there being no such thing as a RuntimePropertyHandle. It should be possible to get hold of property getter or setter methods, of course.

    2) it should be methodof not infoof (avoiding the confusing in-foof pronunciation that most people will read it as)

    3) you can put freaky syntaxes inside the methodof(...) braces - a comma separated list of typenames is NOTHING - after all, typeof(..) supports the crazy 'angle-bracketed comma-delimited list of optional whitespace tokens' syntax for generic types (e.g. typeof(Action<,,,,>). I'd accept things like Class.PropertyName.get, Class.EventName.add, Class.this[int].get... the proposals above about verbose syntaxes are fine.

    ultimately, what's possible in the syntax should pretty much be determined by what IL supports as ldtoken arguments in terms of methodrefs, methodspecs, and so on.

  • Hi Eric, I'm enjoying the back-story behind diferent feature decisions. I enjoyed the User / Eric conversation :)

    Kirk

  • Not sure how I feel specifically about infoof().....

    But 100% certain that I despise ANYTHING that requires me to use a string to represent information. The lack of compile time validation has probably caused more bugs (at least in "successful" builds, not necessarily in releases) than any other single factor since I have been programming in C#.

    Because of this, our mandate for testing (which has to be manuaklly written) when using any feature that is string based, costs significant $$$ [we estimate $10-$25 per usage!!]

    If anything can be done to provide alternatives which do not rely on strings, it would trump almost every other item on "my" backlog (including most of the 3.5 and 4.0 features]

  • Like Paul Batum said, why doesn't the team set up an "infoof" project on codeplex, promote it and open it up to everyone to cover the accepted 80 % classes with the current syntax? this could help the team in also seeing where they'd need to focus their attention when this is discussed again...

  • Totally enjoyed the dialog driven format of this post. Definitely drives home the point. But the experience could have been enhanced by simply substituting "Booger" (aka Dudley Dawson), Mr Peabody, Beavis, or any other more palpable character in place of "User". Maybe next time?

    Indeed. I considered making the dialogue between Simplicio and Sagredo, or Achilles and the Tortoise, but thought that doing so might distract from my point.

    I confess that writing a dialogue about minor points of programming language design as a conversation between Beavis and Butt-head had not occurred to me; I encourage you to try the form yourself and report back how well it works. -- Eric

  • I can attest to the trickiness of this problem.

    I have been developing a library to help me generate type safe MSIL with Reflection.Emit, which define classes to wrap the various FooBuilder and FooInfo classes, and encapsulate calls to ILGenerator.Emit with the correct OpCode and validating that the types are consistent. And I have built an associated tool, that produces a library of static readonly properties to represent the members of a given class as objects from my library. Currently, the support for generics and private/protected members in this tool leaves much to be desired, and I find it is a hard problem to even define what the correct behavior is, even when the use case is only my personal use.

  • I previously mentioned some hypothetical C# fieldof and methodof operators, which would obtain FieldInfo and MethodInfo objects, in the same way that typeof retrieves Type at runtime. Eric Lippert explains the pros and cons of a combined infoof operator,

  • @James

    > it should never return a PropertyInfo, only a MethodInfo - there being no such thing as a RuntimePropertyHandle. It should be possible to get hold of property getter or setter methods, of course.

    I think you're putting the implementation before the usage scenarios. There are many cases where what we want is the property - in fact, I dare say that such cases would probably make the majority. If a feature would be implemented that does not achieve this, it's next to useless, no matter how efficiently it's implemented.

    To be honest, I don't care much about efficiency for such things anyway (they aren't likely to be used in tight inner loops, or other scenarios where the hit would be noticeable). If it's as slow as reflection, it's fine by me. The only thing I really want is compile-time checking and type safety.

    Looking at dependency properties again, the code:

    class Foo {

     public static readonly DependencyProperty BarProperty = DependencyProperty.Register("Bar", typeof(int), typeof(Foo));

     public int Bar { ... }

    }

    Now there are quite a few points where it can go wrong. We need to be sure that the string "Bar" actually matches the name of the property Bar; we need to make sure that registered type for the property matches the actual type; and we need to make sure that owner type is correct. All of these are not in any way checked statically. Furthermore, refactoring tools won't catch them, either - if you rename Bar or change its type, you have to hunt all registered dependency properties manually, without help from auto-rename or "Find Usage".

    Now compare to something like this:

    class Foo {

     public static readonly DependencyProperty BarProperty = DependencyProperty.Register(infoof(int Foo.Bar));

     public int Bar { ... }

    }

    Both name and type of property are checked at compile-time. Owner type is obtained from PropertyInfo automatically.

  • I think it is quite "natural" that the design process for a feature supposed to give information about some existing language feature X reveals lots of corner cases if X itself has corner cases (like the "odious overloads" examples). In other words, the "infoof" design problems reveal design problems inside the space "of" refers to.

  • I think, it's wrong to propose reflection as an alternative to "nameof" (or whatever you call it) and the example at the end of the story to be honest  is not realistic.

    Not realistic? Upon what data do you base that conclusion? Do you, like me, also get to see thousands of emails from C# users describing what features they'd like to see, and their real-world scenarios that drive those feature requests? Do you get input from focus groups? Do you get together with a few hundred MVPs once a year and talk to them about possible new features and how they'd use them? If you don't do these things then how do you know what is "realistic" for the industry as a whole? -- Eric

    Why do I need "nameof" operator? Mostly just for two things.

    First, to retrieve the name of the variables for assertions (how many ArgumentExceptions you throw in your code? how often you refactor your code?).

    Next, for attributes. Current framework architecture requires to write method or property name as a string when it is needed to reference it in attribute. I'd like to see more checks in this area at complie-time due to possible refactorings, typos, etc. So, as for me something like  

    [ItemsSource(nameof(MyClass, Items))]

    class MyCLass { public IList Items {get; } }

    would be quite ok.

    Will it be possible to use infoof in cases like this? Can it be used within existing framework infrastructure that requires compile time string input?  I'm a little afraid that while trying to solve all the possible issues the feature will not be usable in the end.

    The attribute case is one of the most common driving scenarios for this feature request. -- Eric

    If there was quite stupid "nameof" operator that just generated the compile time string from resolved item name or error in case of any ambiguities it would be much much more better than now when we deal with such errors in runtime.

    Indeed, it is always easier to make a weak feature that doesn't do anything difficult. The down side is that weak features still cost a lot, and you end up with a language full of weak features. -- Eric

Page 2 of 4 (53 items) 1234