Реализация сложения в наследовании классов с обобщениями - PullRequest
1 голос
/ 03 сентября 2010

Со структурой ..

abstract class Unit
{
 int Id;
}

class Measure : Unit
{
 int Current;
 int Baseline;
}

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

Я в основном хочу добавить метод «Добавить» для добавления, скажем, двух мер вместе или добавления двух весов вместе. Но это должно быть в базовом классе Unit. Так что в основном, если бы у меня было

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.
      ); 
} 

Я пытался ..

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

в классе Unit, но я получаю ошибки о том, что это не соответствующий идентификатор в унаследованных классах, когда я пытаюсь заполнить "T" соответствующим классом. Есть идеи?

Ответы [ 4 ]

4 голосов
/ 03 сентября 2010

Вам нужно изменить свой Unit абстрактный класс на универсальный тип:

abstract class Unit<T>

Затем вы можете добавить Add абстрактный метод:

void Add(T unit);

Так что вашклассы измерения и веса теперь будут выглядеть так:

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

В качестве альтернативы, добавьте следующий абстрактный метод к Unit:

abstract void Add(Unit unit);

И тогда вам нужно будет ограничить это, используя типпроверка в вашем наследующем классе:

void Add(Unit unit)
{
    if (unit.GetType() != this.GetType())
    {
        throw new ArgumentException("You can only add measurements.");
    }
}
0 голосов
/ 04 сентября 2010

Если вам абсолютно необходимо было использовать два списка базового класса:

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");
    }
}

... затем реализуйте следующее в каждом производном классе:

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

   //actual implementation
}

затем расширьте эту функциональность в ваших производных классах с помощью фактической реализации Add.

Честно говоря, я не вижу смысла в том, что вы пытаетесь сделать. Это не сработало бы, если бы оба списка не содержали только Меры или только Веса, и вы просто скрываете этот факт от потребителя, помещая проверку типов в Unit. Это просто напрашивается на неприятности.

Я бы определил код, в котором у вас есть эти два списка, следующим образом:

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

... затем используйте общий метод Unit.Add () следующим образом:

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

Вы должны использовать списки, строго типизированные по весу или измерению. Вы можете попытаться преобразовать универсальный параметр List, используя Linq:

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

Конечно, это приведет к тому, что веса будут отфильтрованы из списка, но это может работать в ваших интересах:

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

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

Приведенный выше код, используемый с общими методами AddLists и Unit.Add (), которые я изложил, будет безопасным для типов и, следовательно, не будет генерировать никаких исключений времени выполнения. Не делает это хорошо; любой новый созданный вами модуль потребует добавления дополнительного кода для обработки каждого нового типа.

0 голосов
/ 04 сентября 2010

Я действительно получил идею от GenericTypeTea, но мне пришло в голову попробовать другой метод.

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();
    }
    }
0 голосов
/ 04 сентября 2010

Если ваша иерархия достаточно стабильна, вы можете использовать идею шаблона посетителя, чтобы придумать что-то вроде этого:

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);
  }
}

Вы даже можете использовать настоящего посетителя и отделить его от класса Unit, если вы ожидаете, что другие варианты поведения будут добавлены в иерархию позже.

Примечание: если Add изменит текущий экземпляр, тогда if должно вернуть void.

...