5

考虑一下:

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接口和Person类:

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; } }
}

我根本不建议进行任何语义更改。

4

9 回答 9

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;}

在一个接口和一个类中看起来是一样的,这并不意味着完全一样的东西。

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

于 2009-03-18T00:53:30.497 回答
4

前段时间我考虑过同样的事情,特别是在你只有一个接口的生产实现但你想模拟它进行测试的情况下使用。目前它最终有点像过去的 .c/.h 文件。

我怀疑最终它的好处会被语言的额外复杂性和之后阅读代码所抵消。不过,我仍然有兴趣看到它进行更彻底的探索。即便如此,我的优先级列表中还有其他更高的事情——更好地支持不变性是最重要的:)

于 2009-03-18T06:25:41.933 回答
3

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

于 2009-03-18T01:13:27.813 回答
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 -> Extract Interface。

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

提取接口:

interface IPerson
{
    string FirstName { get; set; }
    string FullName { get; }
    int ID { get; }
    string LastName { get; set; }
}
于 2009-03-18T01:07:35.427 回答
1

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

这:

IPerson iperson = new Person();

在 C# 中已经是合法的。

编辑:为澄清起见 - 鉴于以下情况,上述代码是合法的:

interface IPerson { }

class Person : IPerson { }
于 2009-03-18T00:41:59.217 回答
1

如果我请求它,我至少希望 Visual Studio 将我的属性从接口实现为自动属性。

不幸的是,这个选项没有,我必须处理未实现的异常存根

于 2009-04-08T14:11:14.327 回答
0

不,因为您将被迫公开接口的所有公共成员。试试ReSharper,再也不用担心这个了。

于 2009-03-18T00:52:49.207 回答
0

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

  1. 您可以先编写您的 Person 类。
  2. 您可以通过将成员拉到 IPerson 界面来提取您的界面。

因此,您可以让 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 会执行同步魔术,自动为您生成这些属性存根。

于 2009-03-18T01:05:25.633 回答
0

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

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(); 
于 2010-11-01T13:35:34.767 回答