在什么情况下有人会将接口作为参数传递(或接收)?它真的是一件有用的东西,还是一种奇特的做事方式?
答案 0 :(得分:18)
这是非常有用的东西。
例如, LINQ extension methods的 。他们不关心传递给他们的是什么,只要它实现IEnumerable<T>
。我们的想法是,它们都可以应用于您可以使用foreach
循环枚举的任何内容。
想象一下,如果他们都要求你传递T[]
数组或List<T>
个对象,那将是多么无谓的限制。
这只是一个非常简单的例子。让我们假装LINQ扩展不存在(如果我使用的是.NET 2.0,这实际上是可能的)并且我想写一个Sum
方法。
我可以这样写:
public static double Sum(List<double> values)
{
double sum = 0.0;
foreach (double value in values)
{
sum += value;
}
return sum;
}
这一切都很好,但在这里注意一些事情:我写了一个方法来获取List<double>
,这是一个具有功能远远超过此代码所依赖的的类。它在哪里使用Insert
?它在哪里使用RemoveAt
? FindAll
? Sort
?不,这不是必需的。那么这个方法是否真的必要才能传递List<double>
?
此外,我说有double[]
。从理论上讲,我应该可以在values
参数中弹出它,因为我所做的只是使用foreach
枚举它;但由于我已将values
键入为List<double>
,要将double[]
传递给Sum
方法,我必须执行此操作:
double sum = Sum(new List<double>(myArray));
这只是一个完全不必要的新对象,我构建它只是为了调用那些本来应该能够处理原始对象的代码。
通过编写将接口作为参数的方法,您可以使代码更灵活,更强大,并且避免施加不适当的限制(给我一个X,即使我可以很容易地用Y < / em>)关于调用代码。
答案 1 :(得分:5)
最容易记住的方法是,它是关于接口编程而不是实现。比方说,我有一个方法,我想做点什么,例如。
public void MakeNoise(IAnimal animal)
{
animal.MakeNoise();
}
我不在乎具体实现是什么,我只知道无论传入什么,我都可以调用MakeNoise()。我编程到一个接口,而不是一个实现。
public class Dog : IAnimal
{
public void MakeNoise()
{
Console.WriteLine("Woof");
}
}
public class Cat : IAnimal
{
public void MakeNoise()
{
Console.WriteLine("Meow");
}
}
接口编程是OOP的核心方面,你会发现它们非常有用。
答案 2 :(得分:2)
每当你需要抽象时。
一个很好的例子是.NET Framework中的IEnumerable<T>
和IQueryable<T>
接口。它们允许您编写可用于List<T
&gt;,Dictionary<TKey, TValue>
甚至T[]
的扩展方法。
您也可以使用依赖注入。在ASP.NET MVC中,通常使用存储库进行数据访问:
public class MyClassRepository
{
public MyClass GetById(int id)
{
// Some Implementation
}
}
public class MyController
{
private MyClassRepository _repo;
public class MyController() : base(new MyClassRepository()) { }
public class MyController(MyClassRepository repo) { _repo = repo; }
}
现在,如果你想模拟用于单元测试的存储库......你就是骨头。没有简单的方法。接口来了!
public interface IMyClassRepository
{
public MyClass GetById(int id);
}
public class MyClassRepository : IMyClassRepository
{
public MyClass GetById(int id)
{
// Some Implementation
}
}
public class MyController
{
private IMyClassRepository _repo;
public class MyController() : base(new MyClassRepository()) { }
public class MyController(IMyClassRepository repo) { _repo = repo; }
}
现在通过引入接口,我们可以自由地模拟IMyClassRepository,但我们认为适合测试目的。通常,这涉及一个具有指定行为的简单模拟对象,以产生可靠的结果。
答案 3 :(得分:1)
接口非常有用。
它们有助于解耦代码 - 例如,如果您使用IList
界面并将其传递给您的方法并在您的方法中使用它,则可以传入任何集合实现此接口,无论它是否在BCL中。
答案 4 :(得分:1)
如果您使用的是design pattern approach,那么界面非常有意义的一个领域。
例如,Observer pattern,Proxy pattern,Visitor pattern等。您可能可以选择不使用接口,但我想你会很快决定代码变得更清晰(如更模块化,关注点分离)而不是非接口代码。在这些情况下,它只是有助于促进更好的代码。
答案 5 :(得分:0)
多态性!
它们可以帮助您编写不区分某些类的代码,因为它们不会从特殊的基类继承。您的职能将是平等机会执行者。