将变量声明为接口类型是一种好/可接受的做法吗?

Lea*_*ner 5 c#

使用接口声明变量是一个好习惯吗?在我的公司,我们讨论过它,我反对它.

例如,我想要一个存储字符串键和字符串值对的集合.我也不想允许重复.很明显,我在我的班级中声明了Dictionary变量,该变量将公开(通过属性).

Dictionary<string, string> myDic;
Run Code Online (Sandbox Code Playgroud)

但是,一个团队成员说这不是一个好习惯!他说你用IDictionary声明一个变量,这将允许消费者分配他们想要的任何集合(实现IDictionary).例如Hashtable或Dictionary

IDictionary myDic;
myDic = new Hashtable(); // Consumer's code
Run Code Online (Sandbox Code Playgroud)

要么

mydic = new Dictionary<string, string>(); // Consumer's code -

我现在可以知道,将变量声明为接口类型真的是一个好习惯吗?当我清楚地知道该变量的预期时,那也是如此?

Jos*_*osh 13

对于局部变量,私有字段等,它只是分裂头发.使用接口类型定义公共属性是有好处的.例如,当我看到一个公开类型属性的类时,我感到畏缩List<string>.

如果您正在使用依赖注入或像MEF这样的组合框架,那么强烈建议使用接口而不是具体实现.它使您可以轻松地将实现与测试实现交换.

  • +1,因为对于本地人/私人来说,它是分裂的头发.此外,我提倡使用`var`,它不会损害可读性/可理解性,因为它进一步减少了代码更改的影响:) (2认同)

Mer*_*ham 6

在您的公共接口上,您通常应尝试将尽可能抽象的类型公开,以有效地满足用户的要求(即使您的"用户"是您自己的代码).

您应该为这些泛型类型分配好的默认值,这样您就可以在代码中尽快开始使用它们,而不必在每次要使用它时创建和分配具体类型.

一个例子:

public class Something
{
  public Something()
  {
    this.Map = new Dictionary<string, string>();
  }

  public IDictionary<string, string> Map { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这允许类或实现者的用户在不破坏现有代码的情况下切换到新的字典类型,但也不要求用户每次使用它时都要编写此代码:

var something = new Something();
something.Map = new Dictionary<string, string>();
Run Code Online (Sandbox Code Playgroud)

他们可以开始使用它:

var something = new Something();
something.Map["test"] = "some value";
Run Code Online (Sandbox Code Playgroud)

其他类型的示例:

public IEnumerable<string> ValuesReadOnly { get; private set; } // Read-only access
public IEnumerable<string> ValuesReadWrite { get; set; } // Sequence read-write access
public IList<string> ValuesRandomReadWrite { get; set; } // Random read-write access
Run Code Online (Sandbox Code Playgroud)

这样做的一个很好的理由是,您可以在将来插入任何类型,而无需先将其转换为您定义的特定类型.没有什么比必须与各种类型进行转换更快地污染代码.

例如,我发现IEnumerable在使用Linq时我使用了很多.如果我有List<T>我的公共成员,我总是必须在分配之前将值转换为列表,这不仅会使我的代码膨胀,而且还会损害性能.你必须在内存中制作列表结构的另一个副本,你不能利用懒惰的评估序列/等东西yield return.

一个例子:

public class Something2
{
  public IEnumerable<string> SomeValues { get; set; }
}

var dbQuery = BuildQuery("select item from inventory where item.Price > 5.00");
var something = new Something();
something.SomeValues = dbQuery.Evaluate(); // Imagine if this did paging...
Run Code Online (Sandbox Code Playgroud)

代替:

public class Something2
{
  public List<string> SomeValues { get; set; }
}

var dbQuery = BuildQuery("select item from inventory where item.Price > 5.00");
var something = new Something();
something.SomeValues = dbQuery.Evaluate().ToList(); // Has to evaluate all of them...
Run Code Online (Sandbox Code Playgroud)