C# Language Design Notes for May 21, 2014 (Part I)

Topics: C# Language Design
Coordinator
May 24, 2014 at 10:26 PM
Edited May 24, 2014 at 10:27 PM

C# Language Design Notes for May 21, 2014

Notes are archived here.

This is Part I. Part II is here.

Agenda

  1. Limit the nameof feature? <keep current design>
  2. Extend params IEnumerable? <keep current design>
  3. String interpolation <design nailed down>

Limit the nameof feature?

The current design of the nameof(x) feature allows for the named entity to reference entities that are not uniquely identified by the (potentially dotted) name given: methods overloaded on signature, and types overloaded on generic arity.

This was rather the point of the feature: since you are only interested in the name, why insist on binding uniquely to just one symbol? As long as there’s at least one entity with the given name, it seems fine to yield the name without error. This sidesteps all the issues with the mythical infoof() feature (that would take an entity and return reflective information for it) of coming up with language syntax to uniquely identify overloads. (Also there’s no need to worry about distinguishing generic instantiations from uninstantiated generics, etc.: they all have the same name).

The design, however, does lead to some interesting challenges for the tooling experience:
public void M();
public void M(int i);
public void M(string s);
WriteLine(nameof(M)); // writes the string "M"
Which definition of M should “Go To Definition” on M go to? When does renaming an overload cause a rename inside of the nameof? Which of the overloads does the occurrence of nameof(M) count as a reference to? Etc. The ambiguity is a neat trick at the language level, but a bit of a pain at the tooling level.

Should we limit the application of nameof to situations where it is unambiguous?

Conclusion

No. Let’s keep the current design. We can come up with reasonable answers for the tooling challenges. Hobbling the feature would hurt real scenarios.

Extend params IEnumerable?

By current design, params is extended to apply to IEnumerable<T> parameters. The feature still works by the call site generating a T[] with the arguments in it; but that array is of course available inside the method only as an IEnumerable<T>.

It has been suggested that we might as well make this feature work for other generic interfaces (or even all types) that arrays are implicitly reference convertible to, instead of just IEnumerable<T>.

It would certainly be straightforward to do, though there are quite a lot of such types. We could even infer an element type for the array from the passed-in arguments for the cases where the collection type does not have an element type of its own.

On the other hand, it is usually bad practice for collection-expecting public APIs to take anything more specific than IEnumerable<T>. That is especially true if the API is not intending to modify the collection, and no meaningful params method would do so: after all, if your purpose is to cause a side effect on a passed-in collection, why would you give the caller the option not to pass one?

Conclusion

Params only really makes sense on IEnumerable<T>. If we were designing the language from scratch today we wouldn’t even have params on arrays, but only on IEnumerable<T>. So let’s keep the design as is.

Part II
May 25, 2014 at 2:15 AM
I'd love to see the hard questions around nameof() get solved, primarily because that would be a step towards methodof() which I severely wish was available in C#.
MethodBase method = methodof(Foo<int>.Bar<string>(int,string,DateTime));
May 25, 2014 at 1:55 PM
On the other hand, it is usually bad practice for collection-expecting public APIs to take anything more specific than IEnumerable<T>.
It is sometimes advantageous to declare that you want something fully evaluated and finite and do not wish to suffer potential performance hit on evaluating IEnumerable (that can be literally anything, including read from DB). In those cases support for IReadOnlyCollection (which is basically a finite IEnumerable) and IReadOnlyList would be nice (as far as I remember those two are implemented by arrays).

However I do agree it might be an edge case.
May 25, 2014 at 5:11 PM
What is the use case or rather the benefit of params IEnumerable<T> versus params T[] when you construct the array instance at the call site anyway? I would've assumed that the parameters at the call site are evaluated lazily.
May 25, 2014 at 6:13 PM
Expandable wrote:
What is the use case or rather the benefit of params IEnumerable<T> versus params T[] when you construct the array instance at the call site anyway? I would've assumed that the parameters at the call site are evaluated lazily.
I think the benefit is to not have to have overloads accepting both T[] and IEnumerable<T> when all you wanted was the latter, which I find myself having to do pretty frequently. No benefit to the caller, though.
May 26, 2014 at 2:44 PM
Halo_Four wrote:
No benefit to the caller, though.
The 'no benefit' is there only if you pass the individual parameters at call site. If you have the collection already (either from LINQ call or wherever you get it) then the benefit is you don't need to convert it to array. Except that if the compiler does it for you, you loose the benefits for not having to have an array... but this is an implementation detail and could be addressed later without breaking the code.
May 26, 2014 at 5:20 PM
JanKucera wrote:
Halo_Four wrote:
No benefit to the caller, though.
The 'no benefit' is there only if you pass the individual parameters at call site. If you have the collection already (either from LINQ call or wherever you get it) then the benefit is you don't need to convert it to array. Except that if the compiler does it for you, you loose the benefits for not having to have an array... but this is an implementation detail and could be addressed later without breaking the code.
Well yes, that's true. I meant from the perspective of people calling the function as a params function.
May 27, 2014 at 11:12 AM
Edited May 27, 2014 at 11:14 AM
Is the nameof() feature limited to class members of the current scope?

Will i be able to use nameof to "reference" instance properties of a type, which I don't have an instance?
Example:
class Customer
{
   public string Forename { get; set; }
}

class CustomerViewModel
{
    [MetadataProperty(typeof(Customer), nameof(Customer.Forename)]
    public string Forename { get; set; }
}
As you can see in my example, MetadataProperty attribute refers the "Forename" property of my Customer class. However, since Forename is an instance property and I don't have an instance of "Customer", I am wondering if this actually works or of there is a way to solve this?

The only way I can think of to solve this statically typed would be a solution using a constant:
class Customer
{
  public const string ForenamePropertyName = nameof(Forename);

   public string Forename { get; set; }
}

class CustomerViewModel
{
    [MetadataProperty(typeof(Customer), Customer.ForenamePropertyName]
    public string Forename { get; set; }
}
However this solution would require a lot of boilerplate code.
Jul 2, 2014 at 12:51 PM
@CSharpTeam Will there be design notes for June posted?
Jul 2, 2014 at 8:48 PM
The C# design meetings have always been held at a regular cadence and more frequent than the ones listed here (source: some C9 video about them), so I'm guessing they're discussing unannounced or longer-term features during the meetings they aren't posting notes from. I wouldn't mind being a fly on those walls as well, but I think they'll come to us with those things when they're ready.
Developer
Jul 2, 2014 at 8:55 PM
I believe we had no meetings in June, mainly due to summer vacations.