Переопределить свойство с производным типом и тем же именем С#
Я пытаюсь переопределить свойство в базовом классе с другим, но производным типом с тем же именем. Я думаю, что это возможно с помощью ковариации или дженериков, но я не уверен, как это сделать?
Следующий код получает ошибку:
Ошибка 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 specific to Sun 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++
Или, возможно, это означает, что это просто невозможно?
Можно ли переопределить с производными типами?
Помогите! :) Любая работа вокруг?
Ответы
Ответ 1
Это не так, как вы этого хотите.
Вы можете создать "новое" свойство с тем же именем:
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, которое затем вызывает промежуточное свойство. Это действительно уродливый хак, и хотя я сделал это, я бы не стал его рекомендовать.
Ответ 2
Попробуйте сделать это с помощью дженериков. Следующий класс предоставляет базовый базовый класс для 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 { }
Привлекательность этого подхода заключается в том, что он позволяет вам возвращать строго типизированные свойства и применять базовое ограничение наследования для типов этих свойств. Методы полностью переопределены; однако следует помнить, что переопределение вполне может потребоваться, чтобы избежать проблем приведения к реализации базового класса.
Ответ 3
Если вы ищете ковариационный маршрут, то что-то вроде этого должно работать:
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 specific to Sun Analysis
public Dictionary<AnalysisType, List<Result>> TempCache;
}
}
Честное предупреждение, полностью не скомпилированный код, и я могу ошибаться :) Но это удар в то, что, я думаю, вы хотите...
О, и я думаю, что это решение только для .Net 4.0, я не думаю, что вы можете сделать это раньше, была введена совместная дисперсия...
Ответ 4
Если SunData совместим по назначению с данными Outerspace (наследует от), вам не нужно изменять тип OuterSpaceData
Ответ 5
Не уверен насчет 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;
};