为什么接口不能指定静态方法?
我知道这个问题已经被一遍又一遍地问了,但是我似乎找不到足够好的答案。 因此,为了明确说明我想知道的内容,我将其分为两个问题:
为什么接口不能具有静态方法签名? 我将尝试先回答以下非回答问题:为什么在世界上我想使用以下命令:我希望能够静态调用
SqliteCodeGenerator
和MssqlCodeGenerator
上的interface ICodeGenerator { // this is the method I would like to be static: string GetDbConnectionType(); } abstract class CodeGeneratorBase : ICodeGenerator { public abstract string GetDbConnectionType(); public void GenerateSomeCode(StringBuilder s) { s.AppendLine("var foo = new " + GetDbConnectionType() + "();"); } } class SqliteCodeGenerator : CodeGeneratorBase { public override string GetDbConnectionType() { return "SQLiteConnection"; } } class MssqlCodeGenerator : CodeGeneratorBase { public override string GetDbConnectionType() { return "SqlConnection"; } }
:interface ICodeGenerator { // this is the method I would like to be static: string GetDbConnectionType(); } abstract class CodeGeneratorBase : ICodeGenerator { public abstract string GetDbConnectionType(); public void GenerateSomeCode(StringBuilder s) { s.AppendLine("var foo = new " + GetDbConnectionType() + "();"); } } class SqliteCodeGenerator : CodeGeneratorBase { public override string GetDbConnectionType() { return "SQLiteConnection"; } } class MssqlCodeGenerator : CodeGeneratorBase { public override string GetDbConnectionType() { return "SqlConnection"; } }
另一方面,这是第二个问题,如果您知道达到上述目标的好选择,那么无论如何...
假设您可以在接口中指定类型必须具有特定的静态方法...您将如何调用它? 多态性通过实例起作用-而静态成员显式不使用实例。
话虽如此,在一种情况下,我可以看到静态接口成员正在工作:通用类型。 例如:
// This isn't valid code...
public void Foo<T>() where T : ICodeGenerator
{
string type = T.GetDbConnectionType();
}
这将在混凝土类型T
上调用静态成员。
我已经写了更多关于此的博客,但是我怀疑这样做的好处并不能证明其复杂性。
在替代方案方面-通常,您将拥有另一个接口,并具有实现该接口的单独类型。 在某些情况下,这种方法效果很好,但在另一些情况下,效果不好。
@JonSkeet:可以在CIL中创建一个静态接口成员,所以恐怕您的第一条陈述会引起误解。 我认为Microsoft团队出于鼓励正确使用接口的设计选择而从C#中省略了它。
获得此功能的最佳方法可能是使用扩展方法,这些扩展方法将使您可以将方法添加到接口的所有继承者或该接口的特定实现中,但是您需要编写一个单独的类来保存扩展的实现。 哪些方法(如果没有计划的话)很容易丢失。
乔恩的答案几乎涵盖了所有内容,因此我的答案仅包括围绕使用.NET配置API的可能工作。 它需要一点语法开销,但确实可以让您静态访问实例。
interface IStorage
{
void Store(string item);
}
static class Storage
{
private static readonly IStorage _instance;
static Storage()
{
var storageTypeString = ConfigurationManager.AppSettings["storageTypeString"];
var storageType = Type.GetType(storageTypeString, true);
_instance = (IStorage)Activator.CreateInstance(storageType);
}
public static void Store(string item)
{
_instance.Store(item);
}
}
如果接口可以指定静态类,则该类的成员可能会有所帮助,以便编译器将该类的成员视为该接口的静态成员。 因此,不必使用静态类Enumerable<T>
来获取Enumerable<T>.Default
,而可以在语法上指定IEnumerable<T>.Default
。
如果接口可以指定某些此类静态方法应该以类似于扩展方法的方式使用,但没有与之关联的奇怪的作用域规则,则将更加有用(因此,接口似乎为该方法提供了多个“便利”重载) 一些成员函数而无需所有实现都提供它们)。
如果将接口方法与这样的功能结合起来,可以声明为“可选”,那将是非常有帮助的,这样,当实现提供一种方法时,将使用该方法,而当扩展方法未提供该方法时,它将被自动替换。 但是,这可能需要更改CLR。
无论如何,因为接口不包含静态类,所以最好的办法就是提供静态类,即使编译器将这些类和接口视为完全独立的实体,接口的用户也会发现这些静态类会有所帮助。
我知道这很旧,但是实际上您可以使用在名称空间之外的静态类中声明的静态函数。
但是他们用你的方式说,你只会在抽象类中使函数静态化
从界面上执行此操作
public static class Interfacefunction{
public static string GetDbConnectionType(this ICodeGenerator me)
{
// this is the method I would like to be static:
// you can even get access to me
return "SQLiteConnection";
}
}
我决定使用的一种变通办法(尽管实际上可能会更好),是使用静态实例而不是静态接口。
而不是:
// does not compile
ISomeInterface {
static void DoSomething();
static bool TestSomething(string pValue);
// etc...
}
static class SomeStaticClass : ISomeInterface {
public static void DoSomething() {
}
public static bool TestSomething(string pValue) {
}
}
定义一个类(如果逻辑在使用它的类之间必须有所不同,则使其通用):
sealed class SomeClass {
public void DoSomething() {
// reusable implementation
}
public bool TestSomething(string pValue) {
// reusable implementation
}
}
并将该类的静态实例提供给您的静态类:
static class SomeStaticClass {
static readonly SomeClass sSomeClass = new SomeClass();
}
唯一的问题是,您必须决定是否将属性公开给静态实例:
static class SomeStaticClass {
static readonly SomeClass sSomeClass = new SomeClass();
public static SomeClass SomeProperty { get { return sSomeClass; } }
}
...
SomeStaticClass.SomeProperty.DoSomething();
if (SomeStaticClass.SomeProperty.TestSomething(someValue))
...
或包装其方法:
static class SomeStaticClass {
static readonly SomeClass sSomeClass = new SomeClass();
public static void DoSomething() {
sSomeClass.DoSomething();
}
public static bool TestSomething(string pValue) {
sSomeClass.TestSomething(pValue);
}
}
...
SomeStaticClass.DoSomething();
if (SomeStaticClass.TestSomething(someValue))
...