开发者

Using member-hiding (`new`) to get more specific return type

开发者 https://www.devze.com 2023-01-06 14:55 出处:网络
I\'m considering to use new to redefine members in subclasses to make more specific return type available. The question is: is this a good idea, or is it asking for troubles?

I'm considering to use new to redefine members in subclasses to make more specific return type available. The question is: is this a good idea, or is it asking for troubles?

The problem to be solves is when there are several "mirrored" class hierarchies, where types from every hierarical layer are referencing to the same level of another hierarchy. It's hard to explain, so here is an example:

class A1 {}
class A2 : A1 {}
class A3 : A2 {}

abstract class B1
{
  protected A1 myA;

  A1 MyA { get { return myA; } }
}

abstract class B2 : B1
{
  new A2 MyA { get { return (A2)myA; } }
}

class B3 : B2
{
  new A3 MyA 
  { 
    get { return (A3)myA; } 
    set { myA = value; }
  }
}

There is code which only handles the abstract "level" 1 (A1 / B1 etc), just as well for every other level. It would be nice when each "level" would see the types as specific as possible. Currently, I defined the types on the top most level and downcast it all the time in the calling code. Sometimes I add another member for some "level" using a开发者_StackOverflownother name, this results in many more redundant members. I want to make it cleaner.

(Generics are not an option here, because B2 wouldn't be useful without knowing B3 (it would need additional interfaces there). And if there are many types (eg. also C1/2/3, D1/2/3 ...) there would be too many generic arguments. Technically it would work, but it just gets very complicated.)

Additionally I will serialize some of these classes (DataContractSerializer) and some will be mapped to a database using NHibernate.

Did anyone already do something like this? Is it recommendable? Are there any other approaches to solve the same kind of problem?

Edit: I changed the code a bit because of the discussion about the casts and the implementation of the properties. This is actually not the question here. I just tried to write as little code as possible to make a short example.


I think you're doing it backwards... downcasts will fail, you should design it with upcasts. Implementation of IEnumerable is the most common example of this pattern.

The most derived type needs to be responsible for creating the most derived result, then virtual methods in the base type can be overridden to return the most derived result via an upcast (which might be implicit).


I would not do it.

It is just to save typing anyway and it will lower your performance since B3.MyA will essentially do return (A3)(A2)this.MyA;. Too many unnecessary casts while you can do b3.MyA as A3 directly only if neccessary.

Also if you forget to initalize MyA in B3 to an object of type A3, your cast will fail, since it will be an object of type A1 or A2 or whatever. You can easily shoot yourself in the leg.

Edit: The point of what I have written is: it is NOT a typesafe way to do it and it never will be. You can minimise the risk of doing that (such as your edited code shows) but the possibility is still there. IMHO the benefits of such a construct do not justify the dangers.

Edit2: Note to your Technically it would work, but it just gets very complicated. in generic approach.

Not so complicated IMHO. For comparison:

public abstract class B1<TA, TC> where TA: A1 where TC: A1
{
    public TA MyA { get; protected set; }
    public TC MyC { get; protected set; }
}

public abstract class B2<TA, TC> : B1<TA, TC> where TA : A2 where TC : A2
{
}

public class B3 : B2<A3, A3>
{
}

Less code, better safety and remember you can always use the using statement if you dislike too much typing:

using ClassicB2 = MemberHiding.B2<MemberHiding.A3, MemberHiding.A3>;
ClassicB2 b3 = new B3();
0

精彩评论

暂无评论...
验证码 换一张
取 消