C# 一个好的实践是否留下了一个空的界面?

C# 一个好的实践是否留下了一个空的界面?,c#,interface,C#,Interface,这次,我要创造一个数学问题。我计划有一个字典,其中键是Levels enum{Easy,Medium,Hard},值应该包含一些关于如何创建问题的配置 例如: BinaryProblemConfiguration + Bound1 : Bound<int> + Bound2 : Bound<int> 具体配置应为: public class BinaryProblemConfiguration : IConfiguration { public

这次,我要创造一个数学问题。我计划有一个字典,其中键是Levels enum{Easy,Medium,Hard},值应该包含一些关于如何创建问题的配置

例如:

BinaryProblemConfiguration
    + Bound1 : Bound<int>
    + Bound2 : Bound<int>
具体配置应为:

public class BinaryProblemConfiguration : IConfiguration
{
    public Bound Bound1 {get;set;}
    public Bound Bound2 {get;set;}
}

public class AnotherProblemConfiguration : IConfiguration
{
    // other stuff
}

这个想法是建立一个名为ConfigurationLevels的字典。这是一个很好的做法,让界面空着,还是意味着我的设计有问题

NET Framework设计指南将此称为“标记”接口,并明确表示这是一个坏主意。它们改为使用自定义属性重新编译

避免使用标记接口(没有成员的接口)

自定义属性提供了标记类型的方法。更多信息 关于自定义属性,请参见编写自定义属性。习俗 如果可以推迟对属性的检查,则首选属性 直到代码执行为止。如果您的场景需要编译时 检查时,您不能遵守此指南


您将在何处单独使用
IConfiguration
的实例?如果有这样的用例:

void Something(IConfiguration configuration) { ... }
那么是的,很好。但是对于空接口,这将是一个有趣的用例。一方面,我想到的是序列化对象,您知道通过该方法序列化的对象必须是IConfiguration,但实际上并不关心IConfiguration的外观:

void SerializeConfiguration(IConfiguration configuration) { ... }
现在,从纯函数的角度来看,这对对象同样有效,但我认为这是一种合理的方式,可以提供编译时机制,强烈建议有人使用此方法序列化配置之外的任何东西


这些接口的另一个常见用法是标记接口,您可以使用反射来查找通过实现公共接口而“标记”的类型。

拥有一个扩展另一个接口但不向其添加任何内容的接口肯定会很有用。例如,可以很容易地想象一个
IImmutableEnumerable
的用例,它继承自
IEnumerable
,但承诺它返回的项目序列不会因为任何原因而改变。需要有一个不会更改的项列表的例程可能会有
IEnumerable
IImmutableEnumerable
的重载。第一个重载可以检查提供的对象实例是否实现了
IImmutableEnumerable
,如果没有,则通过复制原始对象中的项来生成一个新的不可变列表;第二个重载可以直接使用传入列表,因为已知它实现了
IImmutableEnumerable


对于一个没有任何成员的接口来说,要想象用例有点困难。这样的接口可用于约束中,以允许例程接受没有其他公共基类型的各种类型,但不幸的是,类层次结构非常复杂,足以使这样的东西在概念上变得有用,因此很难持久化满足这些约束的对象。

如果您确定接口永远不会有任何方法,那么它有什么用途?如果它只是将一个类标记为某个“类型”,请使用一个属性。请参阅C#中的属性和Java中的注释为元数据提供了方法,使“标记接口”过时。我对如何声明它有点困惑?你能告诉我怎么改吗?附言,这条规则只适用于专业级图书馆。如果你正在为自己或你的公司破解一些东西,你可以使用标记接口。我知道创建空接口看起来很奇怪。也许我错了,但它仍然显示我在IConfiguration中有一个错误,我会将“有趣”改为“糟糕”。空接口与它的用途相矛盾。正如Jonathan Alles所指出的,这就是属性的作用。啊,但是检查一个对象是否有接口要比检查一个对象的类型是否有属性快得多。@diggingforfire我至少可以想到一个使用空接口的用例。。。乔纳森·艾伦:你是说性能方面的问题?听起来像是微观优化,并不能证明我滥用接口是正当的。@ChrisShain我最近写了一个序列化程序,我使用了属性。属性特别有用的是,我可以通过获取所有具有可序列化属性的类,在加载时为我的可序列化类型预先设置一组信息。我不确定这在接口上是否可行。由于属性也可以应用于属性和字段,因此还可以指定从对象序列化的内容。对于接口,这是不可能的,除非您的所有属性的类型都实现了接口,这将非常奇怪。此合同的更改是否违反了?接口
IEnumerable
承诺枚举请求将产生一些序列。它不保证持有对实现的引用的代码是否可以使用该引用修改序列,也不保证将来所有的枚举请求都会产生相同的序列。如果
IImmutableEnumerable
(源自
IEnumerable
)承诺所有未来的枚举将产生相同的序列,那么这样的承诺不会与
IEnumerable
的任何承诺相矛盾。请注意,
IEnumerable
…的许多实现无法合法地实现
IImmutableEnumerable
,因为它们不能保证将来所有的枚举都返回相同的结果。还要注意的是,实现
IImmutableEnumerable
的任何类型,或者从实现IImmutableEnumerable的类型继承的任何类型,都不能合法地提供任何更改由此封装的序列的方法。
IEnumerable
的许多合法实现不会实现
IImmutableEnumerable
不是LSP违规行为
void Something(IConfiguration configuration) { ... }
void SerializeConfiguration(IConfiguration configuration) { ... }