了解接口

cho*_*bo2 16 .net c# asp.net-mvc interface

我仍然无法理解哪些接口有益.我阅读了一些教程,但我仍然不知道它们的真正含义是"他们让你的课程保持承诺"和"他们帮助多重继承".

多数民众赞成.我仍然不知道什么时候甚至可以在实际工作示例中使用接口,甚至何时确定何时使用它.

根据我对接口的有限知识,他们可以提供帮助,因为如果某些东西实现了它,那么你可以通过接口允许传入类似不同的类,而不必担心它不是正确的参数.

但是我从来不知道真正的意义是什么,因为他们通常在这一点上停止显示代码在通过界面后会做什么,如果他们这样做,似乎他们没有做任何有用的事情,我可以看看并去"哇,他们会在现实世界的例子中帮助".

所以我想我要说的是我正在尝试找到一个真实世界的例子,我可以看到界面在行动.

我也不明白你可以做像这样的对象的引用:

ICalculator myInterface = new JustSomeClass();
Run Code Online (Sandbox Code Playgroud)

所以现在如果我去myInterface点和intellisense会拉起来我只会看到接口方法而不是JustSomeClass中的其他方法.所以我还没有看到这一点.

我也开始进行单元测试,他们似乎喜欢使用接口,但我仍然不明白为什么.

比如这个例子:

public AuthenticationController(IFormsAuthentication formsAuth)
{
    FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
}

public class FormsAuthenticationWrapper : IFormsAuthentication
{
    public void SetAuthCookie(string userName, bool createPersistentCookie)
    {
        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }
    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}

public IFormsAuthentication FormsAuth
{
    get;
    set;
}
Run Code Online (Sandbox Code Playgroud)

喜欢为什么要打扰这个界面呢?为什么不使用其中的方法制作FormsAuthenticationWrapper并将其称为一天?为什么首先创建一个接口然后让Wrapper实现接口然后最后编写方法?

然后我不明白这句话真的在说什么.

就像我现在知道声明说的那样

FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
Run Code Online (Sandbox Code Playgroud)

如果formsAuth为null,则创建一个新的FormsAuthenticationWrapper,然后将其分配给作为Interface的属性.

我想这可以追溯到为什么参考的东西.特别是在这种情况下,因为所有方法都完全相同.Wrapper没有接口没有的任何新方法,我不确定但是当你这样做时,方法被正确填充(即它们有一个体),它们不会被转换为存根,因为这看起来真的没有意义对我来说(它将被转换回界面).

然后在测试文件中他们有:

var formsAuthenticationMock = new Mock<AuthenticationController.IFormsAuthentication>();
Run Code Online (Sandbox Code Playgroud)

所以他们只是传递FormsAuthentication,我猜测所有的假存根.我猜测程序实际运行时会使用包装类,因为它有真正的方法可以执行某些操作(比如签署一个人).

但是看看新的Mock(来自moq)它会接受一个类或一个接口.为什么不再让包装类把那些方法放进然后在新的Mock调用中呢?

这不只是为你制作存根吗?

Cod*_*key 17

好吧,我起初也很难理解,所以不用担心.

想一想,如果你有一个课程,那就说是一个视频游戏角色.

public class Character
{
}
Run Code Online (Sandbox Code Playgroud)

现在说我希望角色拥有武器.我可以使用一个接口来确定武器所需的方法:

interface IWeapon
{
    public Use();
}
Run Code Online (Sandbox Code Playgroud)

所以让我们给角色一个武器:

public class Character
{
    IWeapon weapon;

    public void GiveWeapon(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public void UseWeapon()
    {
        weapon.Use();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建使用IWeapon接口的武器,我们可以将它们提供给任何角色类,并且该类可以使用该项目.

public class Gun : IWeapon
{
    public void Use()
    {
        Console.Writeline("Weapon Fired");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以把它粘在一起:

Character bob = new character();
Gun pistol = new Gun();
bob.GiveWeapon(pistol);
bob.UseWeapon();
Run Code Online (Sandbox Code Playgroud)

现在这是一个普遍的例子,但它提供了很多力量.如果查找策略模式,您可以阅读更多内容.

  • 伟大的例子伴侣.我也试图学习使用Interface,但到目前为止我还很难做到这一点.这个简单但正如你所说,非常有力的例子,只是给了我一些quidlines.真喜欢它.干杯. (2认同)

Joe*_*orn 14

接口定义合同.

在您提供的示例中,??如果您传递null给构造函数并且实际上与接口没有任何关系,则运算符只提供默认值.

更相关的是你可能使用一个实际的FormsAuthenticationWrapper对象,但你也可以实现自己的IFormsAuthentication类型,它与包装类完全无关.界面告诉您实现合同需要实现哪些方法和属性,并允许编译器验证您的对象是否真的履行了该合同(在某种程度上 - 在名称中遵守合同很简单,但在精神上却不是这样) ,所以FormsAuthenticationWrapper如果你不想,你不必使用预先构建的.您可以构建一个完全不同但仍然遵守所需合同的不同类.

在这方面,接口很像正常继承,有一个重要的区别.在C#中,类只能从一种类型继承,但可以实现许多接口.因此,接口允许您在一个类中完成多个合同.对象可以 IFormsAuthentication对象,也可以是IEnumerable.

从另一个方向看它时,界面更有用:它们允许您将许多不同的类型视为完全相同.一个很好的例子是各种集合类.拿这个代码示例:

void OutputValues(string[] values)
{
   foreach (string value in values)
   {
       Console.Writeline(value);
   }
}
Run Code Online (Sandbox Code Playgroud)

这接受一个数组并将其输出到控制台.现在应用这个简单的更改来使用接口:

void OutputValues(IEnumerable<string> values)
{
   foreach (string value in values)
   {
       Console.Writeline(value);
   }
}
Run Code Online (Sandbox Code Playgroud)

此代码仍然采用数组并将其输出到控制台.但它也需要一个List<string>或任何你关心的东西来实现它IEnumerable<string>.因此,我们已经采取了一个接口,并用它使一个简单的代码块多的功能更加强大.

另一个很好的例子是ASP.Net成员资格提供者.您告诉ASP.Net您通过实现所需的接口来履行成员资格合同.现在,您可以轻松自定义内置的ASP.Net身份验证以使用任何源,这一切都归功于接口.System.Data命名空间中的数据提供程序以类似的方式工作.

最后要注意的是:当我看到一个带有"默认"包装器实现的界面时,我认为它有点像anit-pattern,或者至少是代码味道.它告诉我,可能界面太复杂了,你要么需要拆分它,要么考虑使用组合+事件+委托的一些组合而不是派生来完成同样的事情.


And*_*are 10

也许了解接口的最佳方法是使用.NET框架中的示例.

考虑以下功能:

void printValues(IEnumerable sequence)
{
    foreach (var value in sequence)
        Console.WriteLine(value);
}
Run Code Online (Sandbox Code Playgroud)

现在,我可以写这个函数接受List<T>,object[]或任何其他类型的具体顺序.但是因为我已经编写了这个函数来接受类型的参数,IEnumerable这意味着我可以将任何具体类型传递给实现该IEnumerable接口的函数.

这是理想的原因是通过使用接口类型,您的功能比其他方式更灵活.此外,您正在增加此功能的实用性,因为许多不同的呼叫者将能够使用它而无需修改.

通过使用接口类型,您可以将参数的类型声明为您传递的任何具体类型所需的合约.在我的示例中,我不关心您传递给我的类型,我只关心我可以迭代它.


Cri*_*urf 5

这里的所有答案都很有帮助,我怀疑我可以添加任何新的东西,但在阅读这里的答案时,两个不同答案中提到的两个概念在我脑海中确实很好地融合在一起,所以我将在这里写下我的理解.希望它可以帮助你.

类具有方法和属性,类的每个方法和属性都具有签名和主体

public int Add(int x, int y)
{
return x + y;
}
Run Code Online (Sandbox Code Playgroud)

Add方法的签名是第一个大括号字符之前的所有内容

public int Add(int x, int y)
Run Code Online (Sandbox Code Playgroud)

方法签名的目的是为方法分配名称,并描述它的保护级别(公共,受保护,内部,私有和/或虚拟),它定义了可以从代码中访问方法的位置

签名还定义了方法返回的值的类型,上面的Add方法返回一个int,以及方法期望调用者传递给它的参数

方法通常被认为是对象可以做的事情,上面的例子暗示了方法定义的类与数字一起使用

方法主体(在代码中)精确地描述了对象如何执行方法名称描述的操作.在上面的示例中,add方法的工作原理是将加法运算符应用于其参数并返回结果.

在语言语法方面,接口和类之间的主要区别之一是接口只能定义methd的签名,而不能定义方法体.

换句话说,接口可以在类的动作(方法)中描述,但它绝不能描述如何执行动作.

现在您希望能够更好地理解界面是什么,我们可以继续讨论问题的第二部分和第三部分,以及为什么我们在实际程序中使用界面.

在程序中使用的主要时间接口之一是当人们想要执行动作而不想知道或者与这些动作如何执行的细节相关联时.

这是一个非常抽象的概念,所以也许一个例子可能有助于巩固你的想法

想象一下,你是一个非常流行的网络浏览器的作者,每天有数百万人使用,你有成千上万的功能请求来自人,一些大,一些小,一些好,一些像"带回来<maquee><blink>支持".

因为您只有少量开发人员,而且当天的工作时间更少,所以您不可能自己实现所有要求的功能,但您仍然希望满足您的客户

因此,您决定允许用户开发自己的插件,这样他们就可以<blink'直到奶牛回家.

要实现这一点,您可能会想出一个类似于以下内容的插件类:

public class Plugin
{
public void Run (PluginHost browser)
{
//do stuff here....
}
}
Run Code Online (Sandbox Code Playgroud)

但是你怎么能合理地实现这个方法呢?你不可能准确地知道每个可能的未来插件将如何工作

一种可能的方法是将Plugin定义为一个接口,让浏览器使用它来引用每个插件,如下所示:

public interface IPlugin
{
void Run(PluginHost browser);
}

public class PluginHost
{
public void RunPlugins (IPlugin[] plugins)
{
foreach plugin in plugins
{
plugin.Run(this);
}
}
}
Run Code Online (Sandbox Code Playgroud)

请注意,如前所述,IPlugin接口描述了Run方法,但没有指定Run如何工作,因为这是特定于每个插件的,我们不希望插件主机关注每个插件的细节.

为了演示类和接口之间关系的"可以是"方面,我将为下面的插件主机编写一个实现<blink>标记的插件.

public class BlinkPlugin: IPlugin
{
private void MakeTextBlink(string text)
{
//code to make text blink.
}
public void Run(PluginHost browser)
{
MakeTextBlink(browser.CurrentPage.ParsedHtml);
}
}
Run Code Online (Sandbox Code Playgroud)

从这个角度你可以看到插件是在一个名为BlinkPlugin的类中定义的,但是因为它也实现了IPlugin接口,它也可以被称为IPlugin对象,就像上面的PluginHost类那样,因为它不知道或不关心该类实际上是什么类型,只是它可以是一个IPlugin

我希望这有所帮助,我真的不打算这么久.