开发者

Implementing Addition in Inheriting Classes with Generics

With the structure..

abstract class Unit
{
 int Id;
}

class Measure : U开发者_如何学Cnit
{
 int Current;
 int Baseline;
}

class Weight : Unit
{
 int Minimum;
 int Maximum;
 int Current;
}

I basically want to add an "Add" method for adding, say, two Measures together, or adding two Weights together. But it needs to be in the Unit base class. So basically if I had

List<Units> units = new List<Unit>();
List<Units> otherUnits = new List<Unit>();

// populate units with various Measures and Weights.
// populate otherUnits with various Measures and Weights.
foreach(Unit u in units)
{
 u.Add( 
         // add something from the otherUnits collection. Typesafe, etc.
      ); 
} 

I have tried ..

public abstract T Add<T>(T unit) where T : Unit;

in the Unit class, but I get errors about how it isn't an appropriate identifier in the inherited classes when I try to fill "T" with the appropriate class. Any ideas?


You need to change your Unit abstract class to take a generic type:

abstract class Unit<T>

Then you can add the Add abstract method:

void Add(T unit);

So your measurement and weight classes will now look like:

class Measure : Unit<Measure>
class Weight : Unit<Weight>

Alternatively, add the following abstract method to Unit:

abstract void Add(Unit unit);

And then you'll need to constraint this using type checking within your inheriting class:

void Add(Unit unit)
{
    if (unit.GetType() != this.GetType())
    {
        throw new ArgumentException("You can only add measurements.");
    }
}


If your hierarchy is stable enough, you can use the visitor pattern idea to come up with something like this:

abstract class Unit {
  public abstract Unit Add(Unit unit);
  public virtual Measure Add(Measure unit) { return unit; }
  public virtual Weight Add(Weight unit) { return unit; }
}

class Measure : Unit {
  public override Measure Add(Measure unit) { 
    // ...
  }
  public override Unit Add(Unit unit) {
    return unit.Add(this);
  }
}

class Weight : Unit {
  public override Weight Add(Weight unit) {
    // ...
  }
  public override Unit Add(Unit unit) {
    return unit.Add(this);
  }
}

You can even use a true visitor and separate it from the Unit class if you anticipate other behaviors to be added to the hierarchy later.

Note: if Add will change the current instance, then if should return void.


I actually got the idea from GenericTypeTea, but it occurred to me to try a different method.

public interface IUnit
{
    T Add<T>(T unit) where T : IUnit<T>;
}

public interface IUnit<T>
{
    T Add(T unit);
}

    abstract class Unit : IUnit
    {
    }

    class Measure : Unit, IUnit<Measure>
    {
    public Measure Add(Measure unit)
    {
        throw new NotImplementedException();
    } 
    }

    class Weight : Unit, IUnit<Weight>
    {
    public Weight Add(Weight unit)
    {
        throw new NotImplementedException();
    }
    }


If you absolutely had to be using two lists of the base class:

public abstract class Unit()
{
    public abstract Unit Add(Unit other);

    public void MatchType(Unit other)
    {
        if(this.GetType() != other.GetType()) 
            throw new ArgumentException("Units not of same type");
    }
}

...then implement the following in each derived class:

public override void Add(Unit other)
{
   MatchType(other);

   //actual implementation
}

then extend this functionality in your derived classes with the actual Add implementation.

Honestly, I do not see the point of what you're trying to do. It wouldn't work unless both lists contained only Measures or only Weights, and you're just hiding that fact from the consumer by putting type-checking into Unit. That's just asking for trouble.

I would define the code you have these two lists in as follows:

public List<T> AddLists<T>(List<T> firstList, List<T> secondList) where T:Unit
{
   //your code to add elements
}

... then use a generic Unit.Add() method as follows:

public static void Add<T>(this T item, T other) where T:Unit
{
   //perform something that works like item += other
}

You'd have to use lists strongly typed to Weight or Measure. You can try to convert a List's generic parameter using Linq:

listOfMeasures = listOfUnits.OfType<Weight>().ToList();

Of course this will cause Weights to be filtered out of the list, but this can work to your advantage:

listOfWeights = listOfUnits.OfType<Weight>().ToList();
listOfMeasures = listOfUnits.OfType<Measure>().ToList();

var addedListOfMeasures = AddLists(listOfMeasures, otherListOfMeasures);
var addedListOfWeights = AddLists(listofWeights, otherListOfWeights);

The above code, used with the generic AddLists and Unit.Add() methods I laid out, will be type-safe and thus won't throw any runtime exceptions. Doesn't make it good; any new Unit you create will require adding more code to this to handle each new type.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜