Closed JustNrik closed 4 years ago
This is quite similar to existing proposals https://github.com/dotnet/csharplang/issues/2548 and https://github.com/dotnet/csharplang/issues/2499.
How would it work under the hood? same as ref structs with
I(Async)Disposable
I don't see how that's comparable. AFAIK, ref struct
s that have the Dispose
method can be used in a using
statement, even though they can't implement interfaces. But that's just syntax sugar, what you're proposing would require having an object that the runtime considers to implement the interface, which is very different.
I don't see how that's comparable. AFAIK,
ref struct
s that have theDispose
method can be used in ausing
statement, even though they can't implement interfaces. But that's just syntax sugar, what you're proposing would require having an object that the runtime considers to implement the interface, which is very different.
I guess "impliclitly" implementing the interface at compile time would be enough, so even casting would actually work as long as the object fullly implements the interface.
For example
public class Person
{
public void DoWork() {}
}
would get compiled into
public class Person : IWorker
{
public void DoWork() {}
}
so it's about writing the analyzer for it and make it compile this way as long as the interface is fully implemented. This would also work for any other interface like IEquatable
@JustNrik So it wouldn't work for types from other assemblies? What exactly are the rules for when the compiler decides to implement the interface?
Also, I believe doing it exactly this way would be a breaking change. Consider having something like:
public class Business
{
public void Employ(Person worker) { }
}
public class DerivedBussiness : Business
{
public void Employ(IWorker person) { }
}
If you have DerivedBussiness
and invoke Employ
with a Person
, it will currently call Business.Employ(Person)
, but if this proposal was adopted, it would call DerivedBussiness.Employ(IWorker)
instead.
Type A is declared in Assembly 1 Interface B is declared in Assembly 2
A has all the members to implement B
How does it work?
Thinking about it, I guess a wrapper type is the way to go. In that case,
public class Person
{
public void Work();
}
would get translated into
public class Person
{
public void Work();
}
public class <IWorker>__Person : IWorker
{
private readonly Person _person;
public (Person person) => _person = person;
void IWorker.Work() => person.Work();
}
so types in other assemblies will benefit from this. And the compiler will only do this if there's not valid overload for your type, except for the interface so it won't be a breaking change for the DerivedBusiness example
The team has expressed a lot of interest in stuff like this and has put up a few very early proposals.
https://github.com/dotnet/csharplang/issues/1711 https://github.com/dotnet/csharplang/issues/164
I'd expect that anything that eventually shakes out will meet the goal of this request but will likely be much more flexible. For example, it seems that the team intentionally wants extension members to be able to participate.
@JustNrik With that implementation, would casting work? E.g. (Person)(IWorker)new Person()
? What about object identity: object.ReferenceEquals(person, (IWorker)person)
?
@JustNrik With that implementation, would casting work? E.g.
(Person)(IWorker)new Person()
? What about object identity:object.ReferenceEquals(person, (IWorker)person)
?
If Person doesn't implement explicitly IWorker but it has its members, then it will be
new <IWorker>__Person(person)
Casting to IWorker would be a narrowing conversion defined on
public static explicit operator IWorker(Person person)
=> new <IWorker>__Person(person);
so (IWorker)new Person()
would get translated into new <IWorker>__Person(new Person())
now casting IWorker to Person would be another narrowing convertion
public static explicit operator Person(IWorker worker)
=> worker is <IWorker>__Person person ? person._person : throw new InvalidCastException();
ReferenceEquals... well it would compare <IWorker>__Person#_Person
with the provided reference, I'm not sure how this one would work at all
Closing as duplicate of (among others) #2548 and #2499.
Basically, a type doesn't need to implement certain interface, implementing their members should be enough.
Example usage:
How would it work under the hood? same as ref structs with
I(Async)Disposable