Свойство C # точно такое же, определено в двух местах - PullRequest
3 голосов
/ 23 апреля 2010

У меня есть следующие классы:

  1. Defect - представляет тип данных, которые можно найти в базе данных
  2. FilterQuery - предоставляет способ запроса базы данных путем установки простых логических фильтров

Оба Defect и FilterQuery реализуют один и тот же интерфейс: IDefectProperties. Этот интерфейс определяет определенные поля, которые находятся в базе данных. Различные классы имеют методы, которые возвращают списки Defect экземпляров. При FilterQuery вы указываете некоторые фильтры для конкретных свойств, реализованных как часть IDefectProperties, а затем запускаете запрос и получаете список Defect экземпляров.

Моя проблема в том, что в итоге я реализовал некоторые свойства точно так же в FilterQuery и Defect. Эти два по сути разные классы, они просто имеют одни и те же свойства. Например:

public DateTime SubmitDateAsDate
{
    get { return DateTime.Parse(SubmitDate); }
    set { SubmitDate = value.ToString(); }
}

Это свойство требуется IDefectProperties, которое зависит от другого свойства, SubmitDate, которое возвращает string вместо DateTime. Теперь SubmitDate реализован по-разному в Defect и FilterQuery, но SubmitDateAsDate точно так же. Есть ли способ, которым я могу определить SubmitDateAsDate только в одном месте, но оба Defect и FilterQuery предоставляют его как свойство? FilterQuery и Defect уже наследуются от двух разных классов, и, по-моему, для них не имеет смысла делить предка. Я также открыт для предложений относительно моего дизайна.

Ответы [ 6 ]

5 голосов
/ 23 апреля 2010

Когда наследование не имеет смысла, композиция обычно имеет смысл.

internal class DefectPropertyInternal
{
  private IDefectproperty dp
  public DefectPropertyInternal(IDefectproperty dp)
  {
     this.dp = dp;
  }

  public DateTime SubmitDateAsDate
  {
    get { return DateTime.Parse(dp.SubmitDate); }
    set { dp.SubmitDate = value.ToString(); }
  }

}

public class Defect : IDefectProperty
{
  private DefectPropertyInternal dpi;

  public Defect()
  {
     this.dpi = new DefectpropertyInternals(this);
  }

 public DateTime SubmitDateAsDate
 {
    get { return dpi.SubmitDateAsDate; }
    set { dpi.SubmitDateAsDate = value; }
 }

}

Таким образом, DefectPropertyInternals реализует все общие функции для FilterQuery и Defect, без необходимости повторять это в каждом классе.

2 голосов
/ 23 апреля 2010

Если наследование не подходит и вы хотите повторно использовать код, вы должны использовать либо композицию (возможно, избыточную для простого описанного вами случая), либо методы расширения .

Методы расширения - отличный способ симулировать множественное наследование в C #, хотя нет никаких свойств расширения ( «возможно, когда-нибудь», говорит Эрик Липперт ). Если вы хотите отказаться от семантики свойств, вы можете сделать это:

public static DateTime GetSubmitDateAsDate(this IDefectProperties defectProperties) {
    return DateTime.Parse(defectProperties.SubmitDate);
}

public static void SetSubmitDateAsDate(this IDefectProperties defectProperties, DateTime dateTime) {
    defectProperties.SubmitDate = dateTime.ToString();
}
2 голосов
/ 23 апреля 2010

Нет, нет способа сделать это так, как вы описали.

Но. Сам факт того, что у вас есть эта проблема, означает, что ваш первоначальный дизайн, вероятно, не оптимален. Я не знаю всей вашей проблемы, поэтому, пожалуйста, не принимайте мой совет слишком серьезно, но сразу приходит на ум избавление от реализации интерфейса в FilterQuery, а скорее заставьте его принять (или выставить) экземпляр Defect, и использовать свойства этого Дефекта для фильтрации. Еще одна вещь, которая может сработать, - это иметь структуру «хранилища», в которой будут все поля данных, и в нее будут включены и Defect, и FilterQuery.

1 голос
/ 23 апреля 2010

Вот идея. Вместо FilterQuery, реализующего IDefectProperties, его конструктор или метод поиска могут принимать объект, реализующий этот интерфейс.

Опция конструктора:

class FilterQuery{
    IDefectProperties defectProperties;
    FilterQuery(IDefectProperties dp){
        defectProperties=dp;
    }
}

Вы устанавливаете объект FilterQuery со свойствами поиска (вместо настройки свойств в FilterQuery) и передаете его в FilterQuery. Вы можете использовать объект Defect или что-то наследуемое от него или что-то еще (этот дизайн открывает много возможностей). Это также сделает FIlterQuery более согласованным, оставляя только ответственность за поиск и возврат результатов.

1 голос
/ 23 апреля 2010

Я согласен с Федором Сойкиным. Вероятно, вам следует использовать прототип Defect в классе FilterQuery. Если это нереально, вы всегда можете опустить свойства ... AsDate в интерфейсе и реализовать их как расширения. Вам придется либо отложить реализации для каждого расширения на основе классов до общей реализации, либо, если вы используете расширение на основе интерфейса, привести к интерфейсу перед использованием метода. Первое, вероятно, имеет смысл, только если методы / свойства сложны или вы используете больше кода, чем просто переопределение их для каждого класса.

public static class DefectExtensions
{
     public static DateTime SubmitDateAsDate( this IDefectProperties source )
     {
            return DateTime.Parse( source.SubmitDate );
     }

     public static void SetSubmitDateAsDate( this IDefectProperties source, DateTime date )
     {
           source.SubmitDate = date.ToString();
     }
}
0 голосов
/ 23 апреля 2010

Почему бы не реализовать SubmitDateAsDate в классе служебных программ и вызвать эту утилиту из каждого класса?

...