你认为“自动接口实现”在.NET / C#中会有用吗?

时间:2009-03-18 00:34:14

标签: c# .net interface automatic-properties language-enhancement

考虑一下:

public class interface Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

而且:

public class StubPerson : IPerson
{
    int ID { get { return 0; protected set { } }
    string FirstName { get { return "Test" } set { } }
    string LastName { get { return "User" } set { } }
    string FullName { get { return FirstName + " " + LastName; } }
}

用法:

IPerson iperson = new Person();

或者:

IPerson ipersonStub = new StubPerson();

或者:

IPerson ipersonMock = mocks.CreateMock<IPerson>();

因此,实际上我们同时宣布 IPerson 界面和类:

public class interface Person : IPerson

您认为在.NET / C#中获得此类支持是否有用?

修改

由于大规模混淆,我认为我需要澄清建议的目的:

如果没有此功能,您必须写:

interface IPerson
{
  int ID { get; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get; }
}

以及:

public class Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

我根本没有提出任何语义上的变化。

9 个答案:

答案 0 :(得分:11)

让我看看我是否明白你在问什么:

为什么我们不能声明一个接口:

interface IPerson
{
    string Name {get;set;}
    int ID {get;set;}
}

实现该接口的类将继承其属性,而不必重新声明它们:

class Person : IPerson { } 
//person now has properties Name and ID

你不能这样做的原因是,即使你的界面代码和你的类代码的文本非常相似,它们意味着非常不同的东西。界面只是说“实现者将有一个带有getter和setter的字符串名称”。这个类说“当调用名字的getter时返回私有字段”。即使您使用自动属性快捷方式让编译器实现该逻辑,它仍然是 logic ,它属于该类。仅仅因为:

string Name {get;set;}

在界面和类中看起来相同,它并不意味着远程相同。

编译器实现任意逻辑以满足您的合同将是非常危险的,而不是在编译时抱怨您没有实现它们。它可能会引入很难追查的错误。当没有定义任何行为时,让编译器回退到默认行为是一个非常非常糟糕的想法。

答案 1 :(得分:4)

我考虑过the same sort of thing a while ago,特别是在你只有一个接口的生产实现的情况下使用,但是你想要模拟它进行测试。目前它最终有点像以前的.c / .h文件。

我最终怀疑它的好处远远超过了语言中的额外复杂性,然后再读取代码。我仍然有兴趣看到它更彻底地探索。即使在那时,我的优先级列表上还有其他更高的东西 - 更好地支持不可变性在顶部:)

答案 2 :(得分:3)

我相信Eiffel在.NET上做了类似的事情,以支持多重继承。类声明自动生成相应的接口。当引用类类型时,编译器主要发出对接口类型的引用。当然,主要的例外是构造函数表达式。

答案 3 :(得分:2)

我认为其他答案将帮助您理解使用界面来抽象不同具体类中的逻辑,我还认为您可以使用VS内置的重构工具完成类似于您想要的操作。

定义你的班级......

public class Person
{
  public int ID { get; protected set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string FullName { get { return FirstName + " " + LastName; } }
}

然后右键单击,选择Refactor - &gt;提取界面。

这将创建一个单独的文件,其中包含类的定义接口,然后您可以模拟接口并相应地实现类。

提取的界面:

interface IPerson
{
    string FirstName { get; set; }
    string FullName { get; }
    int ID { get; }
    string LastName { get; set; }
}

答案 4 :(得分:1)

我想我错过了这一点 - 你通过将一个类和一个接口混合在一起实现了什么?你用这种方法解决了什么问题?

此:

IPerson iperson = new Person();

在C#中已合法。

修改:为了澄清 - 上面的代码是合法的,具体如下:

interface IPerson { }

class Person : IPerson { }

答案 5 :(得分:1)

如果我要求它,我至少喜欢Visual Studio从接口实现我的属性作为自动属性。

不幸的是,这个选项没有,我必须处理Not Implemented Exception stubs

答案 6 :(得分:0)

不,因为您将被迫公开界面的所有公共成员。试试ReSharper,再也不用担心这件事。

答案 7 :(得分:0)

Resharper可以提供此功能,例如,

  1. 您可以先编写Person类。
  2. 您可以通过pulling members up将您的界面提取到IPerson界面。
  3. 因此,您可以让Visual Studio自动生成实现存根。

    <强>更新

    无论如何,让我们首先阐述接口,引用您在问题中提供的代码:

    public class interface Person : IPerson
    {
        int ID { get; protected set; }
        string FirstName { get; set; }
        string LastName { get; set; }
        string FullName { get { return FirstName + " " + LastName; } }
    }
    

    您必须了解接口不是抽象类。一个接口只是一个契约,一个分类的蓝图,这意味着它会告诉一个对象在另一个对象中期待什么而不关心它是如何实现的。

    另一方面,抽象类可以包含可以继承和覆盖的功能片段。

    在上面的情况中,您的“界面”无效,因为:

    • 您无法在接口(公共,私有,受保护,内部)上声明范围限制,因为这是一个实现细节
    • 您无法声明默认实施(例如,您的FullName属性),因为这是一个实施细节

    在我看来,你真正想要的是一个抽象类,例如,

    public abstract class BasePerson
    {
        public abstract int ID { get; protected set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public virtual string FullName { get { return FirstName + " " + LastName; } } 
    }
    

    我只是猜测,但也许这就是你真正需要的。

    更新2

    好吧,我想我已经达到了你想要发生的事情,所以你想要的是能够写下这个:

    public interface IPerson
    {
        int ID { get; set; }
        string FirstName { get; set; }
        string LastName { get; set; }
        string FullName { get; }
    }
    

    然后对于您的实现,只需要写下这个:

    public class Person : IPerson
    {
        public int ID { get; protected set; }
        public string FullName { get { return FirstName + " " + LastName; } } 
    }
    

    无需指定FirstName和LastName属性。

    我们需要解决的第一个问题是接口在其实现中不允许访问分隔符:会发生什么情况是属性将继承默认的访问分隔符,这是私有的。

    第二个事实是,虽然在我们看来,界面中的string FirstName { get; set; }和类中的public string FirstName { get; set; }是相同的,但事实并非如此:

    • 在接口中,属性定义将指示getter和/或setter方法的方法签名将可用于实现该接口的所有类。
    • 在类中,属性定义将指示CLR创建一个匿名对象,该对象将保存所述属性的值。

    程序员的细微差别,编译器的世界分开。

    最后,当您指定实现接口时,Visual Studio会执行synctactic魔术,自动为您创建这些属性存根。

答案 8 :(得分:0)

我认为更好的抽象是一个特征,或者,正如我所描述的here,一个角色。这就像一个代码接口。您的示例可以这样编码:

public role RPerson { 
  int ID { get; protected set; } 
  string FirstName { get; set; } 
  string LastName { get; set; } 
  string FullName { get { return FirstName + " " + LastName; } } 
} 

public class Person : RPerson { }

public class StubPerson : RPerson { 
    int ID { get { return 0; protected set { } } 
    string FirstName { get { return "Test" } set { } } 
    string LastName { get { return "User" } set { } } 
    string FullName { get { return FirstName + " " + LastName; } } 
} 

// ...

RPerson rperson = new Person(); 

RPerson rpersonStub = new StubPerson();