这一次,我要创造一个数学问题。我计划有一个词典,其中键是Levels enum {Easy,Medium,Hard},值应该包含一些关于如何创建问题的配置。
例如:
BinaryProblemConfiguration
+ Bound1 : Bound<int>
+ Bound2 : Bound<int>
Bound有两个属性:min和max。
其他类型的问题不需要Bounds,但需要其他数据。
所以,我在想创建一个名为IConfiguration的接口。
public interface IConfiguration {}
具体的配置应该是:
public class BinaryProblemConfiguration : IConfiguration
{
public Bound Bound1 {get;set;}
public Bound Bound2 {get;set;}
}
public class AnotherProblemConfiguration : IConfiguration
{
// other stuff
}
我们的想法是拥有一个名为ConfigurationLevels的字典。这是一个很好的做法让界面空了或者我的设计意味着错误吗?
答案 0 :(得分:8)
.NET Framework设计指南将其称为“标记”界面,并且肯定地说这是一个坏主意。他们改为使用自定义属性进行推荐。
避免使用标记接口(没有成员的接口)。
自定义属性提供了一种标记类型的方法。欲获得更多信息 有关自定义属性,请参阅编写自定义属性。习惯 当您可以推迟检查属性时,首选属性 直到代码执行。如果您的方案需要编译时 检查,你不能遵守这个准则。
http://msdn.microsoft.com/en-us/library/ms229022.aspx
public sealed class ConfigurationAttribute : Attribute {
}
[ConfigurationAttribute]
public class AnotherProblemConfiguration : IConfiguration
{
// other stuff
}
答案 1 :(得分:2)
您将在哪里使用IConfiguration
的实例?如果有这样的用例:
void Something(IConfiguration configuration) { ... }
然后是的,很好。但是使用空接口,这将是一个有趣的用例。另外,想到的是序列化对象,你知道通过该方法序列化的对象必须是IConfiguration,但你实际上并不关心IConfiguration的样子:
void SerializeConfiguration(IConfiguration configuration) { ... }
现在从纯粹的功能角度来看,这对Object也有效,但我认为这是提供编译时机制的一种合理方式,强烈建议除了使用此方法的配置之外,有人不会序列化任何内容
这些的另一个常见用法是标记接口,您可以使用反射来查找通过实现公共接口来“标记”的类型。
答案 2 :(得分:1)
拥有一个扩展另一个接口但不添加任何内容的接口绝对有用。例如,可以很容易地想象IImmutableEnumerable<T>
的继承自IEnumerable<T>
的用例,但承诺它返回的项目序列永远不会因任何原因而改变。需要包含不会更改的项列表的例程可能会导致IEnumerable<T>
和IImmutableEnumerable<T>
重载。第一个重载可以检查提供的对象实例是否实现IImmutableEnumerable<T>
,如果没有,则通过复制原始项中的项来生成新的不可变列表。第二个重载可以直接使用传入列表,因为知道实现IImmutableEnumerable<T>
。
有点难以想象一个没有任何成员的界面的用例。这样的接口可以在约束中使用,以允许例程接受没有其他公共基类型的各种类型,但不幸的是,类层次结构足够复杂以使这样的事物在概念上有用,这使得很难保持满足这些约束的对象。