确保所有类共享相同的方法和字段

时间:2017-02-19 22:22:45

标签: c# .net

我的数据访问层我想使用DTO并确保所有网关(表数据网关)都将实现返回特定于给定网关的某些DTO列表的方法。 为了实现这一点,我创建了界面,就像这样

public interface IDataTransferObject{}

这样的所有DTO实现了这个界面

public class UserAccountTypeDTO : IDataTransferObject
{
    public int Id { get; set; }
    public int AccountTitle { get; set; }
    public int CreditTypeId { get; set; }
    public bool Active { get; set; }
}

然后我为所有网关制作了通用接口

public interface IDefaultGateway<D> where D:IDataTransferObject
{
    List<D> Read(SqlDataReader sqlReader);
}

最后通过我的网关实施了IDefaultGateway

class UserAccountTypeGW : IDefaultGateway<UserAccountTypeDTO>
{
    private const string Table = " UserAccountType ";

    private string SelectAll = "select * from" + Table + "";
    private string SelectById = ...;


    public List<UserAccountTypeDTO> Read(SqlDataReader sqlReader)
    {
        throw new NotImplementedException();
    }
}

即使他们不共享任何行为,是否可以将空接口仅以某种方式组合在一起?

网关中的字段只是其值正在变化。我确实将字段复制并粘贴到每个网关,我想知道是否有更快/更懒的方式来执行此操作。

  • 界面和字段不是朋友,因此可以将其定义为属性。由于需要在每个DTO中创建构造函数来初始化属性并且还使set set私有化,结果会比我做的更糟糕。
  • 抽象类与接口
  • 几乎相同
  • 创建基类,定义字段和方法,并在网关构造函数中初始化(或覆盖)它们在这种情况下更好,或者与我所做的几乎相同?

3 个答案:

答案 0 :(得分:3)

您需要搜索的字词称为Marker Interfaces。这是MSDN的指南:

  

使用标记接口(没有成员的接口)进行避免。   如果您需要将类标记为具有特定特征(标记),通常使用自定义属性而不是接口。

话虽如此,检查一个类是否实现接口比检查它是否具有属性更容易。

正如@Groo在本回答的评论部分提到的那样,这是一个非常好的观点:

  

与属性

相比,Marker接口至少也提供了一些编译时检查

答案 1 :(得分:1)

关于问题的第二部分(如何避免代码重复),这就是抽象类的用途。

您可以提供一些默认值,派生类可以根据需要覆盖这些值:

abstract class BaseGateway<T> : IDefaultGateway<T> where T : IDataTransferObject
{
    readonly string _tableName;
    readonly string _selectAll;

    public BaseGateway()
    {
        // default table name
        _tableName = this.GetType().Name.Replace("DTO", "");
        _selectAll = $"select * from {_tableName}";
    }

    // these members are virtual, so that they can be overriden
    protected virtual string TableName => _tableName;
    protected virtual string SelectAll => _selectAll;

    // derived classes should implement their own 'Read' method
    public abstract List<T> Read(IDataReader sqlReader);
}

但是,请注意,执行此类操作会使您的代码容易受到SQL注入攻击。最好使用ORM,或至少使用微型&#34; ORM如Dapper

Dapper通过几种简单的IDbConnection扩展方法为您提供了这种简单性:

public class Dog
{
    public int Age { get; set; }
    public string Name { get; set; }
}            

using (IDbConnection conn = OpenConnection())
{
    var dog = conn
        .Query<Dog>("Select * from Dog where Age = @Age", new { Age = 10 })
        .FirstOrDefault();
}

答案 2 :(得分:1)

避免使用这些标记接口。现在使用标记接口的唯一“好”理由是,如果您计划使用反射操作某些类型的对象(这次不是这种情况)。

在这种情况下 - 抽象类似乎是最好的选择。使用默认实现在抽象类和属性中定义Read()方法。根据需要在所有后代中使用virtual方法覆盖它。这样你甚至不需要一个包装类来读取每个特定类型的对象,因为你将能够引用该对象并直接在其上调用Read()