Reputation: 4167
probably a no-brainer, but please take a look at the following classes / Interfaces:
public interface IChallenge
public interface IChallengeView<T> where T : IChallenge
{
T Challenge {get;set;}
}
public interface IChallengeHostView
{
IChallengeView<IChallenge> ChallengeView { get; set; }
}
public class AdditionChallenge : IChallenge {}
public class AdditionChallengeView: IChallengeView<AdditionChallenge> {}
The scenario is a didactic app for young children. I intend to keep the application flexible by separating the host (which could be any graphical surrounding) from the challenge that is to be solved. That way I could use the same surroundings to host addition, multiplication,division...
Now, when I want to fill this with some life, I get a conversion issue:
HostView hostView = new HostView(); // implements IChallengeHostView
AdditionChallengeView challengeView = new AdditionChallengeView();
hostView.ChallengeView = challengeView;
This, of course, does not work. I see why it doesn't but I have no clue whatsoever how to get around this.
Any ideas?
UPDATE : I had decided to post as little code as possible before, but that brought me into the trouble of hiding one issue from you guys: The interface IChallengeView has a settable property (now visible in the code above), which makes covariance impossible to apply here - The generic type parameter can only be invariant in that case.
The answer given by rich.okelly is correct, but based on false assumptions (which, again, were based on the poor level of detail given by my description here).
I decided to make the code a little less implementation-type-adhesive, like so:
public interface IChallenge
public interface IChallengeView
{
IChallenge Challenge {get;set;}
}
public interface IChallengeHostView
{
IChallengeView ChallengeView { get; set; }
}
public class AdditionChallenge : IChallenge {}
public class AdditionChallengeView: IChallengeView {}
That means I have some more casting code in the AdditionChallengeView (and all other implementing classes), but it seems to me that this is the only viable way at the time.
Upvotes: 2
Views: 133
Reputation: 81307
It is often useful to separate out portions of an interface that use a type parameter in covariant fashion and those which use one i contravariant fashion. This often requires the use of a "SetProperty" method rather than a read-write property (for whatever reason, if an interface inherits an interface which includes a read-only property Foo
, and another which implements a write-only property Foo
, the compiler will say any attempts at property access are "ambiguous" and won't allow foo
to be read or written, notwithstanding the fact that read accesses can only refer to the read-only property and write accesses to the write-only property. Nonetheless, separating out contravariant and covariant aspects of an interface will often allow one to use variance in the cases where it would be helpful and make sense. Further, separating out the portions of an interface which read an object is often helpful anyway.
One minor note: I would suggest that when using an interface one use the following terms to have the indicated meanings:
An a "readable" foo interface should provide a means for reading the characteristics of an object, but should make no promise about whether the object might be writable using some other other means.
A "read-only" foo interface should not only provide a means for reading the characteristics of an object, but should also promise that one may expose a reference to any legitimate implementation without exposing a means of writing to the object. There is no promise, however, that there isn't some some other means by which the object might be modified.
An "immutable" foo interface should promise that any property which is observed to have a given value will always have that value.
If code needs to simply read out what's in an object, it can ask for an "IReadableFoo". If code is using an object reference for short-term encapsulation data which it wants to expose to other code, but isn't allowed to expose the object itself to anything that might modify it, it should wrap the object in a read-only wrapper unless it can safely expose the object directly (which would be indicated by the object implementing IReadOnlyFoo
. If code wants to persist a reference as a means of persisting a snapshot of the data therein, it should make a copy of the object if there's any possibility that it might change, but shouldn't bother if the object will always be the same (indicated by IImmutableFoo
).
Upvotes: 0
Reputation: 41767
If you're using c#4 (or above) you can take advantage of variance. Try declaring your IChallengeView<T>
interface as covariant like so:
public interface IChallengeView<out T> where T : IChallenge {}
Upvotes: 1