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

发布于 2024-07-15 18:41:28 字数 1503 浏览 6 评论 0原文

考虑这个:

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

我根本不建议任何语义改变。

Consider this:

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

And this:

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

Usage:

IPerson iperson = new Person();

Or:

IPerson ipersonStub = new StubPerson();

Or:

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

So in effect we are declaring the IPerson interface and the Person class at the same time:

public class interface Person : IPerson

Do you think it would be useful to have this kind of support in .NET/C#?

Edit:

Due to mass confusion I think I need to clarify the proposed purpose:

Without this feature you would have to write:

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

as well as this:

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

I'm not proposing any semantic change at all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

愁杀 2024-07-22 18:41:29

我想我没有抓住重点——通过将类和接口混合在一起你要完成什么? 您用这种方法解决什么问题?

这:

IPerson iperson = new Person();

在 C# 中已经合法。

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

interface IPerson { }

class Person : IPerson { }

I guess I am missing the point - what are you accomplishing by mixing a class and an interface together? What problem are you solving with this approach?

This:

IPerson iperson = new Person();

is already legal in C#.

Edit: For clarification - the code above is legal given the following:

interface IPerson { }

class Person : IPerson { }
枫以 2024-07-22 18:41:29

如果我要求的话,我至少希望 Visual Studio 将接口中的属性实现为自动属性。

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

I'd at least like Visual Studio to implement my properties from an interface as auto properties if I request it to do so.

Unfortunately this option doesn't and I have to deal with Not Implemented Exception stubs

不顾 2024-07-22 18:41:29

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

No, because you would be forced to expose all public members of an interface. Try ReSharper, and never worry about this again.

Hello爱情风 2024-07-22 18:41:29

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

您必须了解接口不是抽象类。 接口仅仅是一个契约,某种蓝图,这意味着它将告诉一个对象在另一个对象中期望什么,而不真正关心它是如何实现的。

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

在上面的情况下,您的“接口”无效,因为:

  • 您无法声明接口的范围约束(公共、私有、受保护、内部),因为这是一个实现细节,
  • 您无法声明默认实现(例如,您的 < code>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; 放; } 在接口和 public string FirstName { get; 放; 类中的 是相同的,但实际上并非如此:

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

对于程序员来说是细微的差别,对于编译器来说却是天壤之别。

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

Resharper can provide this functionality, e.g.,

  1. You can write your Person class first.
  2. You can extract your interface by pulling members up to the IPerson interface.

Consequently you can have Visual Studio auto-generate implementation stubs for you.

UPDATE

Anyway, let's expound on interfaces first, citing the code you provided in your question:

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

You have to understand that an Interface is not an Abstract Class. An interface is merely a contract, a blueprint of sorts, which means that it will tell an object what to expect in another object without really caring about how it is implemented.

An Abstract Class, on the other hand, can contain snippets of functionality that can be inherited and overridden.

In the case above, your "interface" is invalid because:

  • you couldn't declare scope constraints on interfaces (public, private, protected, internal) as that is an implementation detail
  • you couldn't declare a default implementation (e.g., your FullName property) because again, that is an implementation detail

It appears to me what you really really want is an abstract class, e.g.,

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

I'm just guessing, but maybe that's what you really need.

UPDATE 2

Okay, I think I'm getting at what you want to happen, so what you want is to be able to write this:

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

And then for your implementation only need to write this:

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

Without needing to specify the FirstName and LastName properties.

First problem that we need to tackle is the fact that interfaces don't allow access delimiters in its implementation: what would happen is that the properties would inherit the default access delimiter, which is private.

Second is the fact that while in our eyes string FirstName { get; set; } in an interface and public string FirstName { get; set; } in a class are the same, they are actually not:

  • in an interface, the property definition will indicate that the method signatures for the getter and/or setter methods will be available for all classes implementing that interface.
  • in a class, the property definition will instruct the CLR to create an anonymous object which will hold the value of the said Property.

Subtle difference for the programmer, worlds apart for the compiler.

Lastly, when you do specify that you are implementing an interface, Visual Studio does perform synctactic magic that automatically makes those property stubs for you.

老子叫无熙 2024-07-22 18:41:29

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

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(); 

I think a better abstraction for this is a trait, or, as I've described here, a role. This is like an interface with code. Your example could be coded like this:

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(); 
一生独一 2024-07-22 18:41:28

让我看看我是否理解您的要求:

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

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

并且实现该接口的类将继承其属性,而无需重新声明它们:

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

您不能这样做的原因甚至是尽管接口代码和类代码的文本非常相似,但它们的含义却截然不同。 该接口只是简单地说“实现者将有一个带有 getter 和 setter 的字符串名称”。 该类表示“调用名称的 getter 时返回私有字段”。 即使您使用自动属性快捷方式让编译器实现该逻辑,它仍然是属于类的逻辑。 仅仅因为:

string Name {get;set;}

在接口和类中看起来相同,但并不意味着完全一样。

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

Let me see if I am understand what you're asking:

Why can't we declare an interface:

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

And classes which implement that interface will inherit its properties without having to re-declare them:

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

The reason you can't do this is even though the text of your interface code and your class code are very similar, they mean very different things. The interface simply says "implementor will have a string Name with getter and setter". It is the class which says "return private field when getter for name is invoked." Even if you use the auto-property shortcut to let the compiler implement that logic, it is still logic, which belongs in the class. Just because:

string Name {get;set;}

looks the same in an interface and in a class, it does not mean even remotely the same thing.

It would be very dangerous for the compiler to implement arbitrary logic to fulfill your contracts for you, instead of complaining at compile time that you haven't implemented them. It could introduce bugs very difficult to track down. Having compilers fall back to default behavior when no behavior is defined is a very, very bad idea.

静若繁花 2024-07-22 18:41:28

我不久前也考虑过同样的事情< /a>,特别适用于您只有一个接口的一个生产实现,但您想模拟它以进行测试的情况。 目前它有点像以前的 .c/.h 文件。

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

I considered the same sort of thing a while ago, particularly for use in the case where you only have one production implementation of an interface, but you want to mock it out for testing. At the moment it ends up being a bit like the .c/.h files of yore.

I suspect in the end that the benefits of it are outweighed by the extra complexity both in the language and then reading the code afterwards. I'd still be interested in seeing it explored more thoroughly though. Even then, there are other things way higher on my priority list - better support for immutability being at the top :)

似狗非友 2024-07-22 18:41:28

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

I believe Eiffel does something like this on .NET, in order to support multiple inheritance. A class declaration automatically produces a corresponding interface. When the class type is referred to, the compiler mostly emits a reference to the interface type instead. The main exception is in constructor expressions of course.

仅冇旳回忆 2024-07-22 18:41:28

好吧,我认为其他答案将帮助您理解如何使用接口来抽象不同具体类中的逻辑,我还认为您可以使用 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 -> 提取接口。

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

提取的接口:

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

Well I think the other answers will help you understand the use of the interface to abstract logic in different concrete classes, I also think you can accomplish something similar to what you want using the refactoring tools built into VS.

Define your class...

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

Then right click, select Refactor -> Extract Interface.

This will create a separate file containing the interface for the definition of the class, you could then mold the interface and implementing classes accordingly.

Extracted Interface:

interface IPerson
{
    string FirstName { get; set; }
    string FullName { get; }
    int ID { get; }
    string LastName { get; set; }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文