Why doesnt this work?
public class ClassOptions {}
public interface Inode {
ClassOptions Options {get;}
}
public class MyClass : Inode {
public ClassOptions Options { get; set; }
}
public class ClassDerivedOptions : ClassOptions {
}
public class MyDerivedClass : Inode {
public ClassDerivedOptions Options { get; set; } << does not implement INode...
}
[ the compiler message tells me why it breaks but i'd like to know the reasoning behind why the compiler doesnt let this through - also if there are any work arounds? - thanks]
It doesn't work because the INode interface explicitly calls for an Options property of type ClassOptions. C# doesn't support return type covariance (which is what you're asking for in this case).
For what it's worth, there's also a language feature request on Microsoft Connect specifically for return type covariance:
Need covariant return types in C# / all .NET languages
If you look at the page, they also mention that the common work-around is to use Explicit Interface Implementation:
public class MyDerivedClass : INode
{
public ClassDerivedOptions Options { get; set; }
public ClassOptions INode.Options { get { return Options; } }
}
As Justin notes, the feature you want is called "return type covariance" and it is not supported in C#, or, for that matter, in the CLR type system.
Though it is frequently requested, it is extremely unlikely (*) that this feature will be implemented any time soon. Since it is not supported in the CLR, in order to implement it we would simply have to generate all the helper methods that do the call forwarding for you. Since you can already do that "manually" with a small amount of code, there is little value added by the compiler doing it for you. (And as another question today notes, people sometimes get confused or irritated when the compiler generates a method to do interface forwarding on your behalf.)
Don't get me wrong; I can see how it comes in handy, and I've used this feature in C++. But every time it has come up in a C# program, I've found I can work around its absence pretty easily.
(*) Of course five years ago I would have said exactly the same thing about named and optional parameters, and now they're in C# 4. It is possible for an unlikely feature to be implemented, but the demand has to be pretty darn high.
It doesn't work because an interface defines a contract and when you implement this contract method signatures must match exactly. A possible workaround is to use a generic interface:
public class ClassOptions
{ }
public class ClassDerivedOptions : ClassOptions
{ }
public interface INode<T> where T : ClassOptions
{
T Options { get; }
}
public class MyClass : INode<ClassOptions>
{
public ClassOptions Options { get; set; }
}
public class MyDerivedClass : INode<ClassDerivedOptions>
{
public ClassDerivedOptions Options { get; set; }
}
The standard way to deal with this situation is to implement the interface explicitly:
public class MyDerivedClass : Inode {
// New, more specific version:
public ClassDerivedOptions Options { get; set; }
// Explicit implementation of old, less specific version:
ClassOptions Inode.Options
{
get { return Options; }
}
}
This is how most old IList implementations worked before generics, for example: specifying a more specific T this[int index] property and then explicitly implementing object IList.this[int index], throwing an exception when the set got called with an object of the wrong type.
In the example code you posted, you don't even need an explicit set, as that is not a member of your Inode interface.
Related
I just found out after trying to implement an immutable interface property, that C# apparently does not support the behavior that I am looking for. What I planned was quite simple, objects would subscribe to an interface that held an immutable generic property, that means a property without a setter. However, to my surprise, a subscribing class can still add the setter without any sort of error. In fact, it appears as though C# interfaces entirely ignore the signature of a property altogether. All it cares about is the property type and name.
For example:
namespace Some.Arbitrary.Framework
{
public interface IIdentifiable<T>
{
/// <summary>
/// Classes that subscribe to this interface
/// may still add a 'set;' signature.
/// </summary>
T Identifier { get; } // the lack of 'set;' is ignored
}
}
I've seen many posts on StackOverflow saying that C# does not support this behavior, but my question is: why does C# not support immutable properties in interfaces like I have described? Is there a fundamental design flaw behind what I am intending to do?
Because interfaces define a contract. When you say that a contract requires a get, you say that the implementer must provide a get, you cannot say "you can only do this", so, given:
public interface IData
{
string Data { get; }
}
You are actually saying
public interface IData
{
string GetData();
}
The client can always do this:
public class Data : IData
{
public string Data { get; set; }
}
Because it's actually doing this:
public class Data : IData
{
public string GetData() {}
public void SetData(string data) {}
}
So, Data implements the IData contract, as far as IData cares.
The most similar structure you could use is this:
public interface IIdentifiable<T>
{
T Identifier { get; }
}
public abstract class IdentifiableBase<T> : IIdentifiable<T>
{
T Identifier { get; protected set; }
}
And make your classes implement IdentifiableBase<T>, or you could just keep the IdentifiableBase class and leave the interface behind altogether.
Is there a specific reason why the following is not possible?
class ClassOfInts : IHaveInts
{
public MyInt IntHolder { get; }
// This solves my use case but i'm unsure why this is necessary
// IInt IHaveInts.IntHolder { get => IntHolder; }
}
interface IHaveInts
{
IInt IntHolder { get; }
}
class MyInt : IInt
{
public int TheInt { get; }
}
interface IInt
{
int TheInt { get; }
}
I would think that the above code successfully implements IHaveInts since MyInt implements IInt.
Is there a specific reason why the following is not possible?
Well, the short answer is: "because the C# specification doesn't allow it". Longer answers typically involve some amount of speculation as to what was in the thought process of the C# language designers. That makes such questions primarily opinion based.
However, they did make a deliberate choice that interface members have to be implemented precisely as declared, and that choice is why you can't do that. A likely reason behind that choice is that they would have to special-case read-only properties, because allowing the property to be implemented that way for a writeable property would be unsafe. Were they to allow that, you'd be able to assign any IInt value to the property which expects only MyInt values.
That said, depending on what you're actually trying to do, you might be able to use generic type variance to support your scenario. This will compile fine:
public class ClassOfInts : IHaveInts<MyInt>
{
public MyInt IntHolder { get; }
}
public interface IHaveInts<out T> where T : IInt
{
T IntHolder { get; }
}
Declared that way, the following works fine:
static void M()
{
IHaveInts<IInt> haveInts = new ClassOfInts();
}
This is semantically equivalent to what you were originally trying to do. That is, when using the interface type, you have a property of type IInt, but you want to implement that property with a member that returns a value of type MyInt.
Just another small C# training app, and just another Compilation Error, but it cannot just go away from me... I am just wondering, what I am doing wrong here:
public abstract class Material
{
}
public abstract class Cloth<T> where T:Material
{
public T Prop { get; set; }
}
public class Cotton : Material
{
}
public class Dress<T> : Cloth<T> where T : Material
{
}
public class Test
{
private Cloth<Material> cloth;
public Test()
{
/* below won't compile */
cloth = new Dress<Cotton>();
}
}
I want to get the base class object from a closed constructed class. Anyone ?
When trying to compile I get the error:
Cannot implicitly convert type Dress<Cotton> to Cloth<Material>
What you want to achieve is called covariance (see the following article for samples).
Unfortunately, there's no variance support for classes: it's restricted to interfaces and delegates.
Thus and alternatively, you might design an interface called ICloth<T> with T covariant:
public interface ICloth<out T>
{
T Prop { get; set; }
}
And implement it in any of your possible cloths, including Cloth<T>.
Now type cloth as ICloth<T> and your assignment should work (i.e. cloth = new Dress<Cotton>();), because Dress<Cotton> is ICloth<out T>, which is an interface with a T covariant generic parameter.
Learn more about generic interface with variance in the following article on MSDN.
This question already has answers here:
The return type of the members on an Interface Implementation must match exactly the interface definition?
(6 answers)
Does C# support return type covariance?
(9 answers)
Closed 8 years ago.
Why can I not do the following?
public class TestClass : TestInterface
{
public ClassX Property { get; private set; }
}
public interface TestInterface
{
InterfaceX Property { get; }
}
public interface InterfaceX
{
}
public class ClassX : InterfaceX
{
}
The TestInterface Property is readonly, thus can only return InterfaceX as per the contract.
However, I get this compiler error:
'TestClass' does not implement interface member
'TestInterface.InterfaceX'. 'TestClass.InterfaceX' cannot implement
'TestInterface.InterfaceX' because it does not have the matching
return type of 'InterfaceX'.
It does not have the matching type but it has a subclass of that type.
I don't know the spec offhand, but I'm sure there's one that explicitly states that return types must match exactly for interface implementations. The closest I can find is 13.4.4:
For purposes of interface mapping, a class member A matches an interface member B when:
A and B are methods, and the name, type, and formal parameter lists of A and B are identical.
A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).
If "type" above means "return type" that would indicate that the return type cannot change.
You could, however, change the return type and explicitly implement the interface with the right return type:
public class TestClass : TestInterface
{
public ClassX InterfaceX { get; private set; }
InterfaceX TestInterface.InterfaceX { get { return InterfaceX; } }
}
UPDATE
According to Eric Lippert it seems to be a CLR limitation, not just a C# one.
You mentioned that you want to expose a reduced set but you want all the functionality internal to the class -- that's not what you want to use an interface for. An interface should only be about your reduced set contract, not also magically function as a full set internally, not without another helper property.
But, there is a way around this limitation while still communicating the contract a little.
interface IExpose<IToolType> where IToolType : ITool
{
IToolType Handler { get; set; }
}
class Expose : IExpose<Tool>
{
public Tool Handler { get; set; }
}
interface ITool
{
}
class Tool : ITool
{
}
I've made a test case to illustrate the problem I've run into.
The first assert passes, but the second and third both fail.
Is there a way to check either of the two failing conditions k in a different way that will work? It would be OK if it's not terribly fast as I intend to cache the results on a per-type basis.
public interface IParentInterface
{
}
public interface IChildInterface : IParentInterface
{
}
public class ParentClass<T> where T: IParentInterface
{
}
public class ChildClass : ParentClass<IChildInterface>
{
}
public class TestClass
{
public ChildClass Property { get; set; }
}
[TestFixture]
public class ScratchPad
{
[Test]
public void Assignabl()
{
var tc = new TestClass();
var tct = tc.GetType();
var pi = tct.GetProperty("Property");
Assert.IsNotNull(pi);
Assert.IsTrue(typeof(ParentClass<IChildInterface>).IsAssignableFrom(pi.PropertyType));
Assert.IsTrue(typeof(ParentClass<>).IsAssignableFrom(pi.PropertyType));
Assert.IsTrue(typeof(ParentClass<IParentInterface>).IsAssignableFrom(pi.PropertyType));
}
}
It is by design that your second assertion fails. When you write
public class ParentClass<ParentInterface>
it actually means that "ParentInterface" is now a symbol for a type argument (doing that is so confusing that, indeed, it totally confused you).
Writing
public class ChildClass : ParentClass<ChildInterface>
then sets yout type argument (yeah, the one named "ParentInterface") to the type ChildInterface. Hence, Childclass is only assignable to ParentClass<ChildInterface>.
Lastly, you should ensure that you follow conventions when definining type arguments, it will confuse you a lot less, e.g.
public class ParentClass<T>
marking interfaces with "I" will also greatly enhance understanding, e.g.
interface IParent { }
interface IChild : IParent { }
I suspect that that which you want is not possible until we get c# 4.0:
Parent<IChild>
is not assignable to
Parent<IParent>
There is currently no co/contravariance for generics.
Isn't this the covariance/contravariance thing?
Then it's just something C# currently does not support, but C# 4.0 might.
You can't, because C# 3.0 does not support variance of this kind. In C# 4.0, you should be able to.
Using another example, say you had a List<ParentInterface>, and could assign it to a List<ChildInterface>:
List<ParentInterface> parentList = List<ParentInterface>();
List<ChildInterface> childList = parentList;
The problem is that the internal storage for parentList is for ParentInterface types. If you derived another interface from ChildInterface:
public interface ParentInterface2 : ChildInterface {}
And then tried to add it to childList like so:
childList.Add(new ParentInterface2Implementation());
You would get an exception, since childList is really a List<ParentInterface> and can only store implementations of ParentInterface, which ParentInterface2 is not.