3.6 KiB
C# Language Design Notes for Mar 8, 2017
Raw notes, yet to be cleaned up - read at your own peril
Xamarin
Interop with Java. Won't be able to surface or implement Java interfaces in C# anymore. There might be Android interfaces we can't implement.
On iOS, they have protocols
protocol Foo
{
void Hello();
@optional
int Color { get; set; }
int Bye();
}
Maps to C# interface
interface IFoo
{
void Hello();
}
(lots more complicated stuff)
We currently have a Swift importer, and that looks horrible.
Proposal
Change: allow the following in interfaces
- member bodies
- static and private methods
- override methods
Not proposing:
- non-virtual public members
- protected and internal members
- nested types
- operator declarations
- etc...
Don't want:
- state!
- conversions
This gives parity with where Java is: they made tweaks over time based on feedback, and this is where they landed (private and static were added).
The more you go to the side of adding things, the more this is a philosophy change for interfaces.
Should overrides be able to call base
? Yes, but would need new syntax, probably. Diamond problem!
Putting a concrete body on your method (etc), means you don't have to implement it on the class.
Important question: is the member inherited into the class, or is it "explicitly implemented"? Our assumption is that it's explicitly implemented. That means you can't "just" refactor similar implementations into an interface the way you do a base class.
Possible syntax for base calls: I.base.M()
.
itf IBase
{
void M() {}
}
itf IDerived : IBase
{
override void M() {} // or
override void I.M() {} // to choose one
}
The diamond problem:
itf IA { int M() => 1; }
itf IB : IA { int M() => 2; }
itf IC : IA { int M() => 3; }
itf ID : IB, IC{} // fine, or a warning, but
cls C : ID {} // must override to choose
A common example may be IDisposable
, and also the Reset
method of IEnumerable
.
For base, it may be that you specifically designate one that has a concrete implementation.
Base syntaxes:
I.base.M()
base(I).M()
base.I.M() // ambiguous with existing meaning
base<I>.M() //
Binary compat
What does the runtime do when an interface adds a default impl and that causes an ambiguity in code that isn't recompiled?
Should this be a runtime error? It would lead to some hard-to-understand failures. Should it "pick one"? That's what Java does, in a deterministic way. Order matters. We have similar issues with variance today, and there we just pick one.
"Why did you not let my program run?" vs "why did you not prevent this hole"?
Let's err on the side of the former: let the program run.
Some semantic challenges
// step 1
itf I { }
cls C : I { public void M(); }
// step 2
itf I { void M() { /* 1 */} }
I i = new C();
i.M(); // does default impl
// step 3: recompile C
I i = new C();
i.M(); // calls C.M();
This is caused by an impedence mismatch btw C# and the runtime. We need to decide if there's something we can do
2
If a class has a member with a signature that matches a member of an implemented interface, we require the rest of the method (e.g., return type) to match. Should the presence of a default implementation change that? No.
Breaks
Should we as a fw team consider these only "theoretical breaks", and allow the fw to add default implementations? Otherwise the value isn't enough.
Java may also have the problem that adding an override in an interface after the fact is a breaking change.
Open questions
Implementation covariant returns