
# C# Language Design Notes for April 1st, 2019

## Agenda

1. Pattern-based Index/Range translation

2. Default interface implementations: Is object.MemberwiseClone() accessible in
an interface?

## Discussion

### Index and Range patterns

The current design for Index has a high implementation overhead for
types that currently have `int` indexers and all of the implementations
sum to delegation to the `int` indexer. Also, full support can only be
added by type authors because there are no "extension indexers" in the
language. There's also a small overhead for using the Index type, as
opposed to doing a "direct" translation into the `int` indexer.

Range is similar to the previous.

Proposal: contextual conversions for Index and Range members

In short, there would be a new contextual language-defined conversion
from `Index` to `int` for any instance member where the receiver is
"indexable".

One significant limitation is the conversion could be more applicable than
expected, meaning that a member could be called with an Index that takes an
int, but the member does not intend to be called with an Index, e.g. a member
that takes a length, not a index, could be called with a System.Index type
due to the containing type being "indexable".

The "contextual" conversion also seems very complicated, especially
given that C# is already very complicated.

*Q: Why only support extensions only up to the type already being
*"indexable"? Could we add support for an extension Count() or similar?*

**A**: Adding general collection functionality is broader than just a different form
of indexing. That seems like an instance of a more general problem in C#.
There are other proposals, like roles, that provide the general purpose
extensibility needed for the above.

*Q: What if Count is implemented slowly or improperly?*

**A**: This is a violation of the framework design guidelines. Count should
be cheap and O(1). We're willing to double down on this guideline.

**Range**

Note: the Slice method is preferred to the Range indexer if the Slice is
present.

This does violate the general principle we have that exact type matches are
always preferred over anything else and it makes adding a Slice method a
breaking library change, since it will be preferred over the Index indexer.
It would also allow a library to add an extension Slice to override the type's
implementation, which is generally not desired.

The general argument of recognizing Slice seems useful and allows people to
add extension support for slicing, which we like.

Note: multi-dimensional arrays are not supported for either

#### Alternative Proposal

We like the general approach of the prior proposal, but think adding contextual
conversions is a step too far. Contextual conversions would be both difficult
for the implementation and for the user to understand.

For Index, if a type is "indexable" (has a Count or Length, and an indexer
that takes an `int`) then a synthetic indexer will be added to the type that
takes an Index and implements a translation to the indexer.

For Range, we do the same thing, but for "rangeable types". A type is
"rangeable" if it has a Count or a Length, and a Slice(int,int) method.

*Q: Is the 'indexable' pattern based on the constructed type or the original
definition?*

Unknown, revisit later.

*Q: Do we want to support Slice(Range) or Slice(int)?*

Probably not -- not worth the complexity.

Note: for the counter-proposal, the length should only be evaluated once, and
after the arguments are evaluated.

Warning or error for writing your own Range indexer?

**Conclusion**

We think the first proposal went too far, but think the alternative is workable.
Let's try to flesh out more of the details of that proposal and then consider it
again for inclusion in C# 8.


### Revisit `object.MemberwiseClone()`

There are two things we'd like to consider: potential uses for the feature
and consistency with the rest of the language rules.

Example of use case:

```C#
interface IPoint
{
    public int X { get; protected set;}
    public int Y { get; protected set;}

    public IPoint WithX(int x)
    {
        var tmp = MemberwiseClone();
        tmp.X = x;
        return tmp;
    }
}
```

One argument also levelled against the feature is that it may be unexpected
that an interface could call MemberwiseClone(). A counter argument is that it
may be unexpected if a base class calls MemberwiseClone, but that is the
current behavior in classes.

On the other hand, many people may have an impression that interfaces and
class are essentially different type hierarchies and they meet at the
implementation. There is also another protected member, the destructor, which
is on object and is questionable to provide to the interface itself.

**Conclusion**

The only thing we strongly agree upon is that many of the members of object
should not have been there in the first place. Beyond that, we aren't
particularly swayed by either the advantages of having the access or the
dangers of misuse. We'll slightly prefer our earlier decision: protected
members of object are not accessible in interfaces.