# C# Language Design Meeting for March 5th, 2025

## Agenda
- [Dictionary expressions](#dictionary-expressions)
- [Target-typed static member lookup](#target-typed-static-member-lookup)

## Quote of the Day

- "I'm sure there are some funky types out there like in old .NET" "I plead the 5th"
- "Give the dot a shot"

## Discussion

### Dictionary expressions

Champion issue: https://github.com/dotnet/csharplang/issues/8659  
Specification: https://github.com/dotnet/csharplang/pull/9158/files#diff-342dab893eaa8c901a2fafe92e2c6c13aaee9e42adcbd9115fac92ac0d1b9a22

We reviewed the proposed conversion and construction rules for concrete dictionary types that don't use `CollectionBuilderAttribute`.

__The conversion rules__ pertain to how we decide that a given type is a dictionary type. For the pertinent situation, where the type is not an interface and does not provide a `CollectionBuilderAttribute`, the proposal requires the type to have an iteration type of `KeyValuePair<TKey, TValue>` for some `TKey` and `TValue`, as well as a matching get-set indexer.

If the type explicitly implemented e.g. `IDictionary<TKey, TValue>` we wouldn't recognize it as a dictionary - the type itself needs to have the right members.

When a type is a dictionary type, collection expressions will be implemented using its indexer rather than `Add` methods.

__The construction rules__ pertain to how we bind to the indexer on such a type.

For _key value pair elements_ `e1:e2` the proposal invokes the setter of the best applicable indexer, based on the key and value expressions in each pair. This means that the invoked indexer is not necessarily the one that qualifies the type as a dictionary type in the first place, by the conversion rules above. An alternative would be to always use the indexer that matches the iteration type - the one that made the type a dictionary type.

For collection expressions we needed more nuance, because there were existing expectations around what would work, based on collection initializers. However, for indexers there is not a corresponding existing initializer case to be consistent with. 

For _expression elements_ `e` and _spread elements_ `..e` the proposed rules require elements to implicitly convert to `KeyValuePair<TKey, TValue>`, exactly matching the iteration type of the dictionary type. There was some concern that this is too restrictive. 

A more permissive alternative would be to allow elements to be `KeyValuePair<...>` ("`KVP`" for short) with different type arguments, as long as those type arguments are implicitly convertible to `TKey` and `TValue` respectively. This would be restrictive in a different way, since the elements would have to _be_ a KVP, not just implicitly convert to one. This could even lead to breaking changes, where ordinary collection expressions have worked for dictionary types in the past due to user-defined conversions to KVP.

A combined alternative would look for any KVP types that the element is implicitly convertible to, and _if there is exactly one_ check that its type arguments have implicit conversions to `TKey` and `TValue`. This would mitigate the potential breaking change, but searching for a unique implicit conversion seems a bit dubious.

#### Conclusion

* The rules for when a type is considered a dictionary are approved as proposed.
* The indexer used in dictionary expressions should be the one that qualifies the type as a dictionary type, not the best fit based on overload resolution.
* In expression and spread elements we'll allow them to be KVPs with different key and value types, as long as those are in turn implicitly convertible to the key and value types of the dictionary. For now the elements have to _be_ KVPs, not just implicitly convertible to them. If that becomes a problem we are open to loosening the rules, but we don't have a good proposal for that yet.


### Target-typed static member lookup

Champion issue: https://github.com/dotnet/csharplang/issues/9138  
Specification: https://github.com/dotnet/csharplang/blob/e2e957687cf2a165fb1a0fb05f1d72ddfbe8f932/proposals/target-typed-static-member-lookup.md

The feature allows the static members of a type to be accessed directly without qualification when the expression is target typed by that type:

``` c#
type.GetMethod("Name", .Public | .Instance | .DeclaredOnly); // BindingFlags.Public | ...
```

The proposal meshes well with some proposals  for discriminated unions, but is useful for today's classes or enums.

It supports the common case where a type has static members _of_ that type. Enums are common, and nested derived types are a way to express DUs today. Also, special values or factories are often exposed this way.

This is a place where a lot of redundancy and repetition exists today, especially when types are large and/or generic. Static usings aren't really an adequate response. They bring the members into scope _everywhere_, which can be very noisy, and for generic types they only allow specific instantiations to be included.

Using a sigil (`.`) in front of the name helps avoid breaking changes or convoluted lookup rules. It helps narrow the field of possible completions and may improve understanding of the code. 

At the same time there's also a lot of resistance to having a sigil instead of just a name. It feels like users may have difficulty realizing _when_ they could choose to use this feature. Without the dot it would feel like the compiler just knows what you mean.

Perhaps there is a path we can take that allows us to get feedback from the ecosystem on this question?

Finally there's also an argument that the whole feature makes code harder to read, and the meaning of names too complex to understand, regardless of syntax.

#### Conclusion

Most of us feel the feature is valuable, and we want to continue investigating and debating. The syntax question is a stumbling block for sure.
