基于实现使用我想要的接口方法

时间:2010-03-06 23:02:52

标签: c# interface

我有两个与界面相关的基本概念,我需要更好 对此事的认知。

1)如果我只想使用接口的某些,我如何使用接口 给定类中的方法?例如,我的 FriendlyCat 类继承自 Cat 并实现 ICatSounds ICatSounds 公开 MakeSoftPurr() MakeLoudPurr() MakePlayfulMeow()。但是,它也暴露了 MakeHiss() MakeLowGrowl() - 这两个我不需要我的 FriendlyCat 类。

当我尝试仅实现接口公开的方法的一些时 编译器抱怨其他人(我不需要)没有 实现。

这个答案是我必须创建一个只包含的界面 我想要公开的方法?例如,从我的 CatSounds 类中,我 会创建 IFriendlyCatSounds 吗?如果这是真的,那么当发生时会发生什么 我想在另一种情况下使用其他方法?我需要创建吗? 另一个定制的界面?这对我来说似乎不是一个好设计。

似乎我应该能够创建一个包含所有内容的接口 相关方法( ICatSounds )然后挑选我选择的方法 我正在使用基于实现的方法( FriendlyCat )。

2)我的第二个问题是非常基本的,但仍然是一个混乱的问题 我。当我实现界面(使用Shift + Alt + F10)时,我得到了界面 “throw new NotImplementedException();”的方法在身体里。什么 除了引用接口方法之外,我还需要做什么 我想在课堂上公开?我相信这是一个很大的概念哎呀,但是 类似于从基类继承,我想获得对方法的访问 通过接口暴露而不添加或更改它们。是什么 编译器期待我实现?

- 编辑 -

我现在明白#1,谢谢你的回答。但我还需要进一步阐述 在#2上。我最初的理解是界面是完全反映的 设计给定类的方法。那是错的吗?所以,如果ICatSounds有 MakeSoftPurr()和MakeLoudPurr(),然后存在这两个函数 CatSounds并做他们所暗示的。然后这个:

public class FriendlyCat: Cat, ICatSounds
{

...

public void ICatSounds.MakeLoudPurr() 
{
 throw new NotImplementedException();
}

public void ICatSounds.MakeSoftPurr()
{
 throw new NotImplementedException();
}

}

实际上反映了已经存在的代码,为什么会这样 我实施什么?为什么我不能这样做:

FriendlyCat fcat = new FriendlyCat();
            fcat.MakeSoftPurr();

如果答案是,正如我所假设的那样,该方法没有 代码因此无能为力。然后,如果我想要这些方法 行为与接口所在的类中的方法完全相同 被命名,我该怎么办?

提前再次感谢...

5 个答案:

答案 0 :(得分:2)

界面是合约。您必须至少为所有方法提供存根。因此,设计一个良好的接口是一个平衡的行为,它具有许多小接口(因此必须使用其中的几个来完成任何事情),并且具有大型,复杂的接口,您只能使用(或实现)部分接口。对于如何选择没有严格的快速规则。

但是你需要记住,一旦你发布了第一个版本的代码,它就会变得更难以改变你的界面 lot 。在设计它们时,最好至少考虑一点。

至于实现,看到存储未编写的方法的代码并抛出NotImplemented异常是很常见的。在大多数情况下,你真的不想发布NotImplemented,但它很好地解决了没有代码编译的问题,因为你还没有实现接口的必需部分。

答案 1 :(得分:2)

在“故意”框架中至少有一个例子没有在类中实现所有接口的契约:ReadOnlyCollection<T>

由于这个类实现了IList<T>,它必须有一个“Insert”方法,这在只读集合中没有任何意义。

微软实施它的方式非常有趣。首先,他们明确地实现了这个方法,如下所示:

public class ReadOnlyCollection<T> : IList<T>
{
    public void IList<T>.Insert(int index, T item)
    {
        throw new NotSupportedException();
    }

    /* ... rest of IList<T> implemented normally */
}

这意味着ReadOnlyCollection<T>的用户在智能感知中看不到插入方法 - 只有先将其转换为IList<T>才会看到它。

必须这样做真的是一个提示,你的界面层次结构有点乱,需要重构,但如果你无法控制接口(或者需要向后兼容性,这可能是MS决定采取的原因)它是一个选项这条路线在框架中。)

答案 2 :(得分:1)

  1. 您必须在界面中实现所有方法。创建两个接口,IHappyCatSounds和IMeanCatSounds,拆分出这些方法。不要在FriendlyCat中实现IMeanCatSounds,因为友善的猫不是卑鄙的猫。您必须将接口视为合同。编写接口时,您可以保证实现接口的每个类都具有这些成员。

  2. 它会抛出NotImplementedException,因为您尚未实现它。编译器期望您实现在猫呜咽,喵喵声或嘶嘶声时完成的代码。接口中没有代码。它只不过是任何实现它的类的契约,所以你不能真正“访问接口实现的代码”,因为接口没有实现任何代码。从接口继承时,实现代码。

  3. 例如:

    // this is the interface, or the "contract".  It guarantees
    // that anything that implements IMeowingCat will have a void
    // that takes no parameters, named Meow.
    public class IMeowingCat
    {
        void Meow();
    }
    
    // this class, which implements IMeowingCat is the "interface implementation".  
    // *You* write the code in here. 
    public class MeowingCat : IMeowingCat
    {
        public void Meow
        {
            Console.WriteLine("Meow.  I'm hungry");
        }
    }
    

    我强烈建议您选择The Object Oriented Thought Process的副本,并完整阅读。这很简短,但它可以帮助你清理。

    首先,我读过thisthis

答案 3 :(得分:1)

想象一下,你可以“挑选。”例如,假设您被允许不在FriendlyCat上实现ICatSounds.MakeHiss()。现在,当您的类的用户写下以下代码时会发生什么?

public ICatSounds GetCat()
{
  return new FriendlyCat();
}

ICatSounds cat = GetCat();
cat.MakeHiss();

编译器必须让这个传递:毕竟,GetCat返回一个ICatSounds,它被分配给一个ICatSounds变量,而ICatSounds有一个MakeHiss方法。但是当代码运行时会发生什么? .NET发现自己调用的方法不存在。

如果允许发生这将是不好的。因此编译器要求您实现接口中的所有方法。如果您愿意,您的实现可以抛出异常,例如NotImplementedException或NotSupportedException:但方法必须存在;运行时必须能够至少调用它们,即使它们爆炸了。

另见Liskov Substitution Principle。基本上,我们的想法是,如果FriendlyCat是ICatSounds,则必须在使用ICatSounds的任何地方进行替换。没有MakeHiss方法的FriendlyCat是不可替代的,因为ICatSounds的用户可以使用MakeHiss方法,但是FriendlyCat的用户不能。

答案 4 :(得分:0)

一些想法:

  1. 接口分离原理。接口应尽可能小,并且只包含无法分隔的内容。由于MakePlayfulMeow()MakeHiss()并非本质上捆绑在一起,因此它们应位于两个不同的界面上。

  2. 您遇到了深度继承树的常见问题,尤其是您所描述的继承类型。也就是说,通常有三个对象具有三种不同的共同行为,但它们中没有一个共享同一组。因此,Lion可能Lick()Roar()Cheetah可能Meow()Lick()AlienCat可能Roar() }和Meow()。在这种情况下,没有明确的继承层次结构是有意义的。由于这些情况,将行为分成单独的类,然后创建组合适当行为的聚合通常更有意义。

  3. 无论如何,请考虑这是否是正确的设计。你通常不会告诉猫咕噜声,你做了一些让它发出咕噜声的东西。因此,而不是MakePlayfulMeow()作为猫的方法,也许在猫上使用Show(Thing)方法更有意义,如果猫看到Toy对象,它可以决定发出适当的声音。换句话说,不要将您的程序视为操纵对象,而应将您的程序视为对象之间的一系列交互。在这种类型的设计中,界面通常看起来不像“可以操作的东西”,更像是“对象可以发送的消息”。

  4. 考虑更接近数据驱动的,可发现的方法而不是更静态的方法。有Cat.MakePlayfulMeow()之类的内容可能更有意义,而不是Cat.PerformAction(new PlayfulMeowAction())。这为一个更通用的接口提供了一种简单的方法,它仍然可以被发现(Cat.GetPossibleActions()),并且有助于解决深度继承层次结构中常见的一些“Lion无法发出的问题”。

  5. 另一种看待事物的方法是不要使接口必须与类定义1:1匹配。考虑一个类来定义是什么,以及一个界面来描述它的功能。因此,FriendlyCat是否应该从某些东西继承是一个合理的问题,但它所公开的接口应该是对其能力的描述。这与我在第三点中建议的“作为消息声明的接口”的概念略有不同,但并非完全不兼容。