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.

  • But Eric, there's one really big advantage of typeof(name) vs GetType("name") ... it's checked at compile time.  This also lets refactoring tools get in on the action.  That's a pretty big reason to go ahead and try to get infoof approved.  I think there'd also be a significant performance delta (because typeof stores a metadata token, not strings and no runtime searching, right?)

    Those are all good reasons to do the feature, yes. -- Eric

    Consider that one of the primary use cases can be expected to be white box testing -- letting the test assembly access private members requires reflection, and it would be really nice if it could use a compile-time checked syntax and enable refactoring.

    Yeah, the syntax may end up not being pretty in order to accommodate those edge cases, for the instance you mentioned you'd need something like:

    infoof(C<T=int>.Bar(T,int))

    But that's still an awful lot more concise than chaining together calls to GetMethod with GetGenericTypeParameters, etc.

    You can probably make your life easier by only searching for bare method names in the current class (where generics names are already in scope), not base classes.  If you want a method from any other class then qualify it.

    Of course, GetMethod and friends have some options -- do you include static members or instance, public only or all visibilities, etc?  How do you request the MethodInfo of a property's getter vs the PropertyInfo?  I wouldn't want to have separate keywords for these so you'd need some syntax for patching them on:

    infoof<method,instance,public>(C<T=int>.Bar(T,int))
    infoof<getter,instance,public>(String.Length)
    infoof<property,static,public>(String.Empty)

    Still an awful lot better than the runtime reflection option.

    Sure, it is better. But look at how much new syntax you just added to the language, for a feature that would get very little mainstream usage, compared to, say, auto props, or query comprehensions. Offer developers a pony and of course they say they want a pony; that's unsurprising. My point is that if we give you the pony, then the unicorns we'd like to give you don't fit into the budget anymore. -- Eric

  • Poor's man infoof:

    class Program {
    static MethodInfo infoof<T>(Action<T> d) {
    return d.Method;
    }
    static MethodInfo infoof<T1, T2>(Action<T1, T2> d) {
    return d.Method;
    }

    static void Main(string[] args) {
    Console.WriteLine(infoof<string>(Console.WriteLine));
    Console.WriteLine(infoof<string, string>(Console.WriteLine));
    }
    }

    Of course, it doesn't cover everything and it has the overhead of creating a delegate but hey, no strings needed!

     

    Cute. There are similar tricks you can do with expression trees; make a lambda that does the call you want, and then extract the method info from the tree. -- Eric

  • Given that the type of infoof(Thing) can already vary between PropertyInfo and MethodInfo, you could also allow it to be IEnumerable<MethodInfo> if the method name is overloaded.

  • > 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.

    I think that may have been true (i.e. the most common case) before WPF shipped, but now? Every time I declare a new dependency property, I have to deal with stringified property names. Ditto for OnPropertyChange methods. A typo anywhere along the line, and a very subtle bug is introduced...

    Frankly, I would be glad to have a "dumb" infoof that would require spelling out every single bit explicitly, with no overload resolution, implicit disambiguation etc ever taking place. E.g.:

     infoof(int C.Bar) // instance property

     infoof(static int C.Bar) // static property

     infoof(void C.Blah(int, int)) // non-generic method taking two ints

     infoof(void C.Blah<T>(T, T)) // generic method

     infoof(void C.Blah<int>(int ,int)) // a specific instantiation of that same method

     infoof(void C.Blah<int>(int ,int)) // a specific instantiation of that same method

     infoof(void C<T>.Blah()) // method of an open generic type

     infoof(void C<int>.Blah()) // method of a closed generic type

    Wordy? Yes, sure, but it solves the "uncaught typo" problem, and doesn't leave the room for ambiguities.

    By the way, wasn't "new" syntax for delegate instantiation in C# 1.0, before assignment was sugared for delegate lvalues and method groups, also lengthy for the same reason (to require the user to explicitly specify the delegate type, and therefore avoid the need to deal with overload resolution)?

  • > Given that the type of infoof(Thing) can already vary between PropertyInfo and MethodInfo, you could also allow it to be IEnumerable<MethodInfo> if the method name is overloaded.

    When you use infoof(), you'd normally want to get one particular method. If you instead get a sequence, and still has to work with reflection methods on those MethodInfo objects to find the method you need (by argument count etc), you might as well just use reflection from the get go. For infoof() to be usable for scenarios where it's desirable, it really needs to be able to unambiguously identify the specific method/property.

  • The perfect is the enemy of the good.

    Point taken. But my point is that the resource-consuming mediocrity is the enemy of the awesome. Like I said before, how many free unicorns are you willing to give up in order to get this free pony? -- Eric

  • @pminaev - yes, but the remaining problems could then be solved with some extension methods defined on IEnumerable<MethodInfo>.

    MethodInfo mi = infoof(SomeMethod).GetOverload<int, string>();

    The overload selection would not be checked at compile time, but at least the name would be.

  • I agree with pminaev. In many ways, I love WPF, but the whole string names for dependency properties and OnPropertyChange is an ugly mess.

    I imagine that all of the reflection that WPF is doing is probably also a performance bottleneck (although I can't back that up).

    Perhaps the real solution to this problem isn't infoof but something like a property delegate.

  • @Daniel

    > yes, but the remaining problems could then be solved with some extension methods defined on IEnumerable<MethodInfo>.

    Not really - you open the same can of worms Eric described (e.g. how do you differentiate between Foo(int,int), and Foo<T=int>(T, T)?), you just move the problems from compiler to library space. But they don't go away.

    @Brad

    > Perhaps the real solution to this problem isn't infoof but something like a property delegate.

    I don't see how it would help for WPF dependency properties. A delegate for an instance property would require an instance (just as a delegate for a method property requires an instance) when constructed using language sugar, and not via reflection. And when you register WPF dependency properties, you do it in static initializers and/or ctor, so you don't have that instance.

  • I no longer pine for infoof now that we have expression trees, but it does seem a little unfortunate that there are now many .NET projects out there with relatively similar chunks of code that inspect the expression tree and give you back the appropriate info instance. What would be nice is if the BCL team would add a small library to do this work. That way when you get people still complaining about the lack of infoof, you can point them to the library and be done with it.

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

    I would have given up pretty much everything in .Net 4 for this. But that's just me - I don't use dynamic and all that, and I can't stand string properties.

    I agree with pminaev - this should be an extremely verbose feature, but it should exist. His syntax is pretty much what I thought of. You treat this feature as syntactic sugar, but there is no way to do this currently except string properties - which are the root of all evil because they are not compile-time checked.

    </rant>

    Off topic: Eric, what about that memoization post you promised two and a half months ago?

  • Wow...where to begin...

    "...nowhere in C# previously did we have a parenthesized, comma-separated list of types. But that’s not at all hard to parse."

    In fact, such a syntax DOES exist: Bar(int, int) is how you specify overload resolution in C# XML documentation, the parser for which is already part of csc. In fact, it is not at all clear to me why the XML doc overload resolver couldn't be turned into infoof exactly as is; surely that would give us our 80% with minimal effort?

    "But look at how much new syntax you just added to the language, for a feature that would get very little mainstream usage, compared to, say, auto props, or query comprehensions."

    If you offered me a choice between either automatic properties and query comprehensions, or infoof, I would pick infoof without hesitation. Query comprehension syntax is nicer to read and write than method syntax in many cases, but not excessively so; and automatic properties are almost useless, especially in the presence of code snippets. Contrast that to infoof, which has NO alternative implementation (and NO, the runtime-evaluated expression tree tricks are nowhere near the same thing), but is critical for avoiding runtime failures when working with metadata, and surely the decision becomes obvious? And yet you and others continue to deprioritize a feature which has been more hotly requested by the community than any other that I have heard of. It is beyond maddening.

    +1 to configurator's comment. If C# 4 had NOTHING but infoof, I would consider that a worthy investment of the C# language team's time.

  • Configurator and commongenius are overstating the matter. However, I would really like to see something like infoof. Even something simpler like nameof(foo.Bar) to aide in PropertyChanged and DependencyProperties would probably take care of 90% of the issues.

    (Unless you fine C# guys have any better ideas to resolve these issues and the others mentioned in this thread?)

  • This largely already exists in library space.  Google up "static reflection" -- e.g. http://www.clariusconsulting.net/blogs/kzu/archive/2007/12/30/49063.aspx -- which makes use of expression trees (as Eric suggested many comments ago, but this idea has been around for over a year).

    It also nicely handles the method overload resolution problem in a nice manner, all without adding additional language complexity.

    The only problem with it is that it's slow -- depending on the types involved, I've seen it take anywhere from 1.5x - 5x slower than normal string-based Reflection.  Acceptable?  Maybe.

    Fastest by far in my testing -- faster than string-based Reflection, in fact -- is Mike's "Poor's man infoof" using the Delegate.Method property.  This effectively bypasses most of the Reflection infrastructure (no Type.GetMember(), etc.), and is the closest we can get to IL member lookup resolution.

    The only problem with it is that it only works for methods, not properties.  (It also shows how much C#'s type inferencing sucks, as if you have e.g. Lamba.Method<T>(Action<T>) and Lambda<T,R>(Func<T,R>) methods, and have a 'static void Foo.Bar(int)' func, Lambda.Method(Foo.Bar) fails to work, even though there's only one method it can possibly match and I'd expect the parameter type to be inferrable.  Here's hoping that C#4 improves things...)

  • Observation: I think the real reason people want an infoof operator is that they don't want to use reflection, even though what they're asking for is available via reflection. That seems to stem from reflection being, well, confusing to use, and a little unwieldy. But like everyone else I write some unit tests, muddle through it, and when it works I have a gin and try to forget.

    Given that, what is the real-world context most people want this stuff for? Enlighten me.

    If anyone's listening, I'd like a data access technology from MS that isn't end-of-lifed after version 1.

Page 1 of 4 (53 items) 1234