Skip to content

Latest commit

 

History

History
76 lines (57 loc) · 3.45 KB

LDM-2024-02-28.md

File metadata and controls

76 lines (57 loc) · 3.45 KB

C# Language Design Meeting for February 28th, 2024

Agenda

Quote of the Day

  • "If we just don't make any mistakes it'll be fine"

Discussion

Extensions

#5497
https://github.com/dotnet/csharplang/pull/7179/commits/520a2a40fc5a0fdb572e5f20f593f9eac59a88da

Today we looked over the proposed set of lookup rules for extensions. Much of the meeting was going over the rules as proposed, which I won't reiterate in these notes; they can be found in the linked pull request commit above. We made a few comments along the way:

  • It may be a bit interesting that you can look up extension members without a qualifier when you're within the extension type that contains the members. We think this is fine, as this is a new context that gets to redefine what this binds to; this for such scenarios is not the underlying type, it's the current extension type.
  • We think that the proposed form of doing lookup when in an extension type as if the extension type's underlying type is an inheritance relationship is the correct decision.
  • Lookup order proved to be a contentious topic. The proposed rules prioritize one type of extension method over the other, but we think that this is likely a mistake. Users may start naturally migrating over to the new extensions slowly, and this may cause potential scenarios where the "wrong" version of an extension is picked; there's no clear answer to us whether the old or new should win beyond the overload resolution rules that we already have in the language. For example:
    static class Extensions
    {
        public static X ToX<Y>(this IEnumerable<Y> values) => ...
    }
    
    implicit extension ImmutableArrayExtensions<Y> for ImmutableArray<Y>
    {
        public X ToX() => ...
    }
    
    // or reverse:
    
    static class Extensions
    {
        public static X ToX<Y>(this ImmutableArray<Y> values) => ...
    }
    
    implicit extension IEnumerableExtensions<Y> for IEnumerable<Y>
    {
        public X ToX() => ...
    }
    For either of these cases, it seems that the best solution is simply to give both ToX methods to overload resolution and let it sort out which one is preferred, erroring if neither is preferred. The precise details of this, and whether there will be any disambiguation of old vs new as a final tiebreaker, will need to come in a future meeting; what we are certain of at this point is that the proposed version, where there is a preference of one version for lookup, isn't workable.
  • There are also some disambiguation scenarios that we will need to consider that can't occur today, such as what will happen in the scenario where a simple name binds to both an extension property and an old-style extension method. We can likely look to similar scenarios that can occur for instance members in metadata today, but it's a scenario to think about. Example:
    class TableIDoNotOwn : IEnumerable<Item> { }
    
    static class IEnumerableExtensions
    {
        public int Count<T>(this IEnumerable<T> t);
    }
    
    implicit extension MyTableExtensions for TableIDoNotOwn
    {
        public int Count { get { ... } }
    }
    
    // What happens here?
    var v = table.Count; // Let's get a read from LDM

Conclusion

Lookup in extension types should treat the underlying type as if it is the base type of the extension type. We need to go back and redesign the rules for lookup of extension members from outside of an extension type to mix old and new style extensions.