Переопределить свойство с производным типом и тем же именем C #

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

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

Ошибка 1 «Sun.Cache»: тип должен быть 'OuterSpace.Cache' для соответствия переопределен member '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> 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> TempCache;
    }
}

public class Moon : OuterSpace {} etc.

Для конечного результата, когда я обращаюсь к объекту "Данные" Sun, я не хочу, чтобы было два объекта данных (Inherited & Base Class), но когда я пытаюсь переопределить свойство, оно требует переменные 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 ++

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

Помощь! :) Есть ли обходной путь?

10
задан Community 23 May 2017 в 11:48
поделиться

5 ответов

Это невозможно, как вы этого хотите, в точности.

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

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 базового класса и перенаправляет на другое защищенное свойство с новым именем, а затем позволяет конечному производному классу добавить «новое» свойство данных, которое затем вызывает промежуточное свойство. Это действительно уродливый хакер, и хотя я сделал это, я бы не рекомендовал его.

11
ответ дан 3 December 2019 в 20:02
поделиться

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

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, я не думаю, что вы можете сделать это раньше, co-variance была введена там...

1
ответ дан 3 December 2019 в 20:02
поделиться

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

0
ответ дан 3 December 2019 в 20:02
поделиться

Не уверен насчет 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
ответ дан 3 December 2019 в 20:02
поделиться

Попытайтесь решить эту проблему с помощью дженериков. Следующий класс предоставляет общий базовый класс для 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 { }

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

7
ответ дан 3 December 2019 в 20:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: