Переопределить свойство с производным типом и тем же именем C # - PullRequest
10 голосов
/ 23 августа 2010

Я пытаюсь переопределить свойство в базовом классе с другим, но производным типом с тем же именем. Я думаю, что это возможно с помощью ковариации или дженериков, но не знаю, как это сделать?

Следующий код получает ошибку:

Ошибка 1 'Sun.Cache': тип должен быть 'OuterSpace.Cache' для соответствия переопределен член 'OuterSpace.Cache'

public class OuterSpace {
    public virtual OuterSpaceData Data {get; set;}
    public virtual OuterSpaceAnalysis Analysis {get; set;}
    public virtual OuterSpaceCache Cache {get; set;}


    public class OuterSpaceData {
        //Lots of basic Data Extraction routines eg
        public virtual GetData();
    }
    public class OuterSpaceAnalysis {
        //Lots of Generic Analysis on Data routines eg
        public virtual GetMean();
    }
    public class OuterSpaceCache {
        //Lots of Caches of Past Analysis Results:
        public Dictionary<AnalysisType, List<Result>> ResultCache;
    }
}

public class Sun : OuterSpace {
    public override SunData Data {get; set;}
    public override SunAnalysis Analysis {get; set;}
    public override SunCache Cache {get; set;}

    public SunData : OuterSpaceData {
        //Routines to specific get data from the sun eg
        public override GetData();
    }

    public SunAnalysis : OuterSpaceAnalysis {
        //Routines specific to analyse the sun: eg
        public double ReadTemperature();
    }
    public SunCache : OuterSpaceCache {
        //Any data cache's specific to Sun's Analysis
        public Dictionary<AnalysisType, List<Result>> TempCache;
    }
}

public class Moon : OuterSpace {} etc.

Для конечного результата, когда я обращаюсь к объекту «Данные» в Sun, я не хочу, чтобы там было два объекта данных (наследуемый и базовый класс), но когда я пытаюсь переопределить свойство, требуется, чтобы переменные Sun были тот же тип, что и базовый класс. Например:

Sun EarthSun = new Sun()
EarthSun.Analyse()  //The OuterSpace Analysis Saves results to Sun Cache:

//Now try use the result:
EarthSun.Cache[0]...

Очень похоже на это, но с производным типом вместо string-array: C # Переопределения переменных-членов, используемые методом базового класса

И этот ответ не имел для меня особого смысла: Как переопределить член базового класса после наследования в C ++

Или, возможно, это означает, что это просто невозможно? Можно ли переопределить с производными типами?

Помощь! :) Любая работа вокруг?

Ответы [ 5 ]

11 голосов
/ 23 августа 2010

Это не так, как вы этого хотите.

Вы можете создать «новое» свойство с тем же именем:

public new SunData Data
{
  get { return (SunData) base.Data; }
  set { base.Data = value; }
}

Это не совсем то же самое, но, вероятно, так близко, как вы собираетесь.

Другой возможный подход:

public SunData SunData { get; set; }

public override OuterSpaceData Data
{
  get { return SunData; }
  set { SunData = (SunData)value; }
}

Преимущество этого подхода состоит в том, что он гарантирует невозможность поместить что-либо в свойство Data, не являющееся SunData. Недостатком является то, что ваше свойство Data не является строго типизированным, и вы должны использовать свойство SunData, если хотите, чтобы оно статически типизировалось как SunData.

Существует отвратительный способ получить «лучшее из обоих миров» за счет того, что он запутан в коде и сложен для понимания впоследствии - путем введения промежуточного производного класса, который выполняет «запечатанное переопределение» для свойства Data базового класса и перенаправляет на другое защищенное свойство с новым именем, а затем имеет конечный производный класс, добавляющий «новое» свойство Data, которое затем вызывает промежуточное свойство. Это действительно уродливый хакер, и хотя я это сделал, я бы не стал его рекомендовать.

7 голосов
/ 23 августа 2010

Попробуйте сделать это с помощью дженериков.Следующий класс предоставляет базовый базовый класс для OuterSpace с ограничениями на его параметры, которые применяют правила наследования для типов свойств.Я упустил методы в маленьких типах для ясности.

public class OuterSpace<TData, TAnalysis, TCache>
    where TData : OuterSpaceData
    where TAnalysis : OuterSpaceAnalysis
    where TCache : OuterSpaceCache
{
    public virtual TData Data { get; set; }
    public virtual TAnalysis Analysis { get; set; }
    public virtual TCache Cache { get; set; }
}
public class OuterSpaceData { }
public class OuterSpaceAnalysis { }
public class OuterSpaceCache { }

public class Sun : OuterSpace<SunData, SunAnalysis, SunCache>
{
    public override SunData Data { get; set; }
    public override SunAnalysis Analysis { get; set; }
    public override SunCache Cache { get; set; }
}
public class SunData : OuterSpaceData { }
public class SunAnalysis : OuterSpaceAnalysis { }
public class SunCache : OuterSpaceCache { }

Привлекательность этого подхода заключается в том, что он позволяет вам возвращать строго типизированные свойства и , обеспечивающие базовое ограничение наследованиятипы этих свойств.Методы полностью переопределены;однако следует помнить, что переопределение вполне может потребоваться, чтобы избежать проблем приведения с реализацией базового класса.

1 голос
/ 23 августа 2010

Если вы ищете ковариационный маршрут, что-то вроде этого должно работать:

public class OuterSpace<TCacheType> where TCacheType : OuterSpaceCache {
    public virtual OuterSpaceData Data {get; set;}
    public virtual OuterSpaceAnalysis Analysis {get; set;}
    public virtual TCacheType Cache {get; set;}


    public class OuterSpaceData {
        //Lots of basic Data Extraction routines eg
        public virtual GetData();
    }
    public class OuterSpaceAnalysis {
        //Lots of Generic Analysis on Data routines eg
        public virtual GetMean();
    }
    public class OuterSpaceCache {
        //Lots of Caches of Past Analysis Results:
        public Dictionary<AnalysisType, List<Result>> ResultCache;
    }
}

public class Sun : OuterSpace<SunCache> {
    public override SunData Data {get; set;}
    public override SunAnalysis Analysis {get; set;}

    public SunData : OuterSpaceData {
        //Routines to specific get data from the sun eg
        public override GetData();
    }

    public SunAnalysis : OuterSpaceAnalysis {
        //Routines specific to analyse the sun: eg
        public double ReadTemperature();
    }
    public SunCache : OuterSpaceCache {
        //Any data cache's specific to Sun's Analysis
        public Dictionary<AnalysisType, List<Result>> TempCache;
    }
}

Справедливое предупреждение, полностью не скомпилированный код, и я могу быть совершенно неправ :) Но это удар в том, что я думаюВы хотите ...

О, и я думаю это решение только для .Net 4.0, я не думаю, что вы можете сделать это раньше, была введена совместная дисперсия ...

0 голосов
/ 23 августа 2010

Не уверен насчет Generics, но вы можете добиться ограниченного эффекта с помощью простого полиморфизма (при условии, что SunData наследует от Data и т. Д.)

public class Sun : OuterSpace 
{
  void SomeInitializationMethod()
  {
    Data = new SunData();
    Analysis = new SunAnalysis(); // etc
  }

    }

// You could also make casting simpler with a generic method on the base
public T GetData<T>()
{
  if (Data is T)
  {
    return Data as T;
  }
  return null;
};
0 голосов
/ 23 августа 2010

Если SunData совместим по назначению с данными Outerspace (наследует от), вам не нужно изменять тип OuterSpaceData

...