4

given the following:

class A : B {}

interface I
{
 B TheObject {get;set;}
}

can I do this somehow?

class C : I
{
 public A TheObject {get;set;} 
}

note the interface has the base class and the implementation has the sub class.

Kyle West
  • 8,934
  • 13
  • 65
  • 97
  • If this were possible it would certainly confuse future maintainers of your code. What is your intent? You should probably refactor if you are running into this issue. – sourcenouveau Jun 07 '09 at 03:48

7 Answers7

13

Give this a try

class C : I
{
 public A TheObject {get;set;} 
 B I.TheObject 
 {
   get { return A; }
   set { A = value as A; }
 }
}

You may need to modify the B setter, depending on your needs. Implementing an interface this way has the following consequences. When dealing with a variable typed as a C you will not be able to access B TheObject. If you need to you will need to declare an I variable and assign it to your C var. Implementing I this way is known as an explicit implementation.

eg

C c = new C();
A a = c.TheObject; // TheObject is an A
I i = c;
B b = i.TheObject;
Kleinux
  • 1,511
  • 10
  • 22
  • Yep, explicit implementation is the way to go IMHO – Thomas Levesque Jun 07 '09 at 15:07
  • Worked perfectly for me. I'm a little unclear why C# doesn't count classes as implementing an interface if they have a property that's a subclass of an interface property. – frodo2975 May 11 '16 at 21:32
  • The `get { return A; }` should be `get { return TheObject; }` right? `A` is a type, not a variable. – Franklin Yu May 25 '18 at 13:43
  • @frodo2975 Actually `public A TheObject { get; set; }` should not implement `B TheObject { get; set; }`. The getter part is sane: `public A TheObject { get; }` does implement `B TheObject { get; }` (but I have no idea why C# compiler does not understand this), but the setter part clearly does not automatically work. Think about it. – Franklin Yu May 25 '18 at 13:48
2

You can't do it because the return type of the interface implementation must match. But, my reasoning behind why you can't do it was wrong (as pointed out in the comments). You must match the signature, but you can return a new A from C ...

public class C : I
{
  public B TheObject { get { return new A(); } set {} }
}
JP Alioto
  • 44,864
  • 6
  • 88
  • 112
  • 1
    I don't quite get your example. Shouldn't it be: A is-a B, but B is not an A? Also, B should be Vehicle and A a Boat, I would be Mobile. Then, another class D, airplane, which would be a Vehicle, cannot be assigned to TheObject because it expects a Boat, instead of a Vehicle as declared in the Mobile interface. Am I missing something? – Ionuț G. Stan Jun 07 '09 at 03:49
1

No, you can't. At least not without C# 4.0's Covariance/Contravariance feature.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
1

Nope. The compiler will complain about the type mismatch.

Also, I'm going to disagree with Paul: I don't think variance will help in your case. Take a look at what Skeet has to say about variance here.

Community
  • 1
  • 1
Esteban Araya
  • 29,284
  • 24
  • 107
  • 141
1

No, assume the following:

class D : B {}

C c = new C();
c.TheObject = new D(); // error. D is not A, A is not D

With the above class I can still use D with objects implementing interface I, but let's assume I want to set TheObject property of an instance of C to an instance of D. I can't, because D and A are siblings. They both inherit from the same class and things would be OK as they are in the interface, but declaring that you expect an A puzzles the compiler. An instance of A is not an instance of D, but instances of both A and D are instances of B.

This rule follows Liskov Substitution Principle.

P.S. I want to thank Troels Knak-Nielsen for opening my mind about this subtlety.

Community
  • 1
  • 1
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
0

What you're thinking of is covariance/contravariance, but even with C# 4.0 this wouldn't work. It still needs to be useable if you only have a reference to the interface.

The getter would work, as A is also a B, but the setter wouldn't work, since the implementation expects an A, while the interface allows you to set TheObject to B. Generally, return values can be subtypes, and arguments can be base types with covariance/contravariance. But only in C# 4.0.

Botz3000
  • 39,020
  • 8
  • 103
  • 127
0

This compiles:

class B { }

class A : B { }

interface I<T> where T : B
{
    T TheObject { get; set; }
}

class C : I<A>
{
    public A TheObject { get; set; }
}

But I assume this is not what you want?

dtb
  • 213,145
  • 36
  • 401
  • 431