C#属性完全相同,定义在两个地方

C#属性完全相同,定义在两个地方,c#,.net,inheritance,properties,C#,.net,Inheritance,Properties,我有以下课程: 缺陷-表示可以在数据库中找到的数据类型 FilterQuery-提供一种通过设置简单的布尔过滤器来查询数据库的方法 Defect和FilterQuery都实现了相同的接口:IDefectProperties。此接口指定数据库中的特定字段。不同的类有返回Defect实例列表的方法。使用FilterQuery,您可以为作为IDefectProperties的一部分实现的特定属性指定一些过滤器,然后运行查询并返回Defect实例的列表 我的问题是,我最终在FilterQuery和Def

我有以下课程:

  • 缺陷
    -表示可以在数据库中找到的数据类型
  • FilterQuery
    -提供一种通过设置简单的布尔过滤器来查询数据库的方法
  • Defect
    FilterQuery
    都实现了相同的接口:
    IDefectProperties
    。此接口指定数据库中的特定字段。不同的类有返回
    Defect
    实例列表的方法。使用
    FilterQuery
    ,您可以为作为
    IDefectProperties
    的一部分实现的特定属性指定一些过滤器,然后运行查询并返回
    Defect
    实例的列表

    我的问题是,我最终在
    FilterQuery
    Defect
    中实现了一些完全相同的属性。这两个类本质上是不同的,它们只是共享一些相同的属性。例如:

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

    这是
    IDefectProperties
    所需的属性,它依赖于不同的属性
    SubmitDate
    ,该属性返回
    字符串而不是
    日期时间。现在
    SubmitDate
    Defect
    FilterQuery
    中的实现方式不同,但是
    SubmitDateAsDate
    完全相同。是否有一种方法可以只在适当的位置定义
    SubmitDateAsDate
    ,但
    Defect
    FilterQuery
    都将其作为属性提供
    FilterQuery
    Defect
    已经从两个不同的类继承,我认为它们共享一个祖先是没有意义的。我也愿意接受关于我的设计的建议。

    不,没有办法完全按照您所描述的那样做


    但是。事实上,你有这个问题意味着你的初始设计可能不是最优的。我不知道您的整个问题,所以请不要太认真地接受我的建议,但我马上想到的是,在
    FilterQuery
    中去掉接口实现,而是让它接受(或公开)一个
    缺陷的实例,并使用该缺陷的属性进行过滤。另一个可行的方法是使用一个“存储”结构,该结构将包含所有的数据字段,缺陷和过滤查询都将包含它。

    当继承没有意义时,组合通常会这样做

    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的所有共享功能,而不必在每个类中重复这些功能。

    如果继承不合适,并且您希望重用代码,那么您必须使用组合(对于您描述的简单情况,可能是过度使用)或

    扩展方法是模拟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();
    }
    

    为什么不在实用程序类中实现SubmitDateAsDate并从每个类调用该实用程序?

    我同意@Fyodor Soikin的观点。您可能应该在
    FilterQuery
    类中使用原型
    Defect
    。如果这不是真的可行,您可以从接口中省略…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();
         }
    }
    

    有个主意。它的构造函数或搜索方法可以接受实现此接口的对象,而不是实现IDefectProperties的FilterQuery

    构造函数选项:

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

    使用搜索属性设置FilterQuery对象(而不是在FilterQuery中设置属性)并将其传递到FilterQuery。您可以使用缺陷对象或从其继承的对象或其他对象(此设计提供了许多可能性),这也会使FIlterQuery更加一致,只剩下搜索和返回结果的责任

    这到底节省了什么?属性必须比这复杂得多,组成才能有更多的用途。不可否认,不是很多。但是它节省了两次实现相同代码的时间(尽管您仍然需要实现接口定义的属性并传递到“internal”类进行处理),我喜欢使用扩展方法的想法。我很想拥有扩展属性(拜托,Lippert!;),但我会尽我所能。我喜欢这种方法,但前提是缺陷是一种值类型(实际上是一种缺陷规范)。如果缺陷是一个实体,那么用它初始化过滤器可能没有意义。这只证明了实体框架的固有局限性:-PI并不意味着任何特定技术-如果
    缺陷
    在概念上是一个实体(即,如果它代表一个缺陷的特定实例),那么,将其用作筛选器或搜索参数可能并不合适。一开始可能很方便,但迟早以这种方式使用可能会产生问题。