Kra*_*ica 435 c# oop language-features
为什么C#这样设计?
据我所知,接口只描述行为,并且用于描述实现某些行为的接口的类的合同义务.
如果类希望在共享方法中实现该行为,为什么不应该这样做呢?
这是我想到的一个例子:
// These items will be displayed in a list on the screen.
public interface IListItem {
string ScreenName();
...
}
public class Animal: IListItem {
// All animals will be called "Animal".
public static string ScreenName() {
return "Animal";
}
....
}
public class Person: IListItem {
private string name;
// All persons will be called by their individual names.
public string ScreenName() {
return name;
}
....
}
Run Code Online (Sandbox Code Playgroud)
Chr*_*org 214
假设你问为什么你不能这样做:
public interface IFoo {
void Bar();
}
public class Foo: IFoo {
public static void Bar() {}
}
Run Code Online (Sandbox Code Playgroud)
从语义上讲,这对我来说没有意义.接口上指定的方法应该用于指定与对象交互的契约.静态方法不允许您与对象进行交互 - 如果您发现自己处于静态实现的位置,您可能需要问自己该方法是否真的属于接口.
public class Animal: IListItem {
/* Can be tough to come up with a different, yet meaningful name!
* A different casing convention, like Java has, would help here.
*/
public const string AnimalScreenName = "Animal";
public string ScreenName(){ return AnimalScreenName; }
}
Run Code Online (Sandbox Code Playgroud)
对于更复杂的情况,您可以始终声明另一个静态方法并委托给它.在尝试提出一个例子的时候,我想不出有什么理由你会在静态和实例上下文中做一些非平凡的事情,所以我会给你一个FooBar blob,并把它作为一个迹象表明它可能不是个好主意.
Mar*_*ett 167
我的(简化)技术原因是静态方法不在vtable中,并且在编译时选择了调用站点.这与您不能拥有覆盖或虚拟静态成员的原因相同.有关更多详细信息,您需要一个CS毕业或编译器 - 我不是.
出于政治原因,我引用Eric Lippert(他是一名编译器,并拥有滑铁卢大学的数学,计算机科学和应用数学学士学位(来源:LinkedIn):
...静态方法的核心设计原则,给它们起名字的原则...... [是] ......在编译时,总是可以确切地确定将调用什么方法.也就是说,该方法可以仅通过代码的静态分析来解决.
请注意,Lippert确实为所谓的类型方法留出了空间:
也就是说,一个与类型(如静态)相关联的方法,它不采用不可为空的"this"参数(与实例或虚拟不同),但调用的方法取决于构造的T类型(不像静态,它必须在编译时可以确定).
但尚未确信其有用性.
小智 91
这里的大多数答案似乎都错过了重点.多态性不仅可以在实例之间使用,也可以在类型之间使用.当我们使用泛型时,通常需要这样做.
假设我们在泛型方法中有类型参数,我们需要用它做一些操作.我们不想立即,因为我们不知道构造函数.
例如:
Repository GetRepository<T>()
{
//need to call T.IsQueryable, but can't!!!
//need to call T.RowCount
//need to call T.DoSomeStaticMath(int param)
}
...
var r = GetRepository<Customer>()
Run Code Online (Sandbox Code Playgroud)
不幸的是,我只能提出"丑陋"的选择:
使用反射 Ugly并击败接口和多态的想法.
创建完全独立的工厂类
这可能会大大增加代码的复杂性.例如,如果我们尝试为域对象建模,则每个对象都需要另一个存储库类.
实例化然后调用所需的接口方法
即使我们控制用作通用参数的类的源,这也很难实现.原因是,例如,我们可能需要实例仅在众所周知的"连接到DB"状态.
例:
public class Customer
{
//create new customer
public Customer(Transaction t) { ... }
//open existing customer
public Customer(Transaction t, int id) { ... }
void SomeOtherMethod()
{
//do work...
}
}
Run Code Online (Sandbox Code Playgroud)
为了使用即时解决方案解决静态接口问题,我们需要做以下事情:
public class Customer: IDoSomeStaticMath
{
//create new customer
public Customer(Transaction t) { ... }
//open existing customer
public Customer(Transaction t, int id) { ... }
//dummy instance
public Customer() { IsDummy = true; }
int DoSomeStaticMath(int a) { }
void SomeOtherMethod()
{
if(!IsDummy)
{
//do work...
}
}
}
Run Code Online (Sandbox Code Playgroud)
这显然是丑陋的,并且不必要使所有其他方法的代码复杂化.显然,也不是一个优雅的解决方案!
sup*_*cat 18
我知道这是一个老问题,但它很有趣.这个例子不是最好的.如果你展示一个用例,我认为会更清楚:
string DoSomething<T>() where T:ISomeFunction
{
if (T.someFunction())
...
}
仅仅能够使用静态方法实现接口将无法达到您想要的效果; 我们需要的是将静态成员作为接口的一部分.我当然可以想象许多用例,特别是在能够创建东西时.我可以提供两种可能有用的方法:
这些方法都不具有吸引力.另一方面,我希望如果CLR中存在的机制能够干净地提供这种功能,.net将允许指定参数化的"新"约束(因为知道类是否具有带特定签名的构造函数似乎难以比较知道它是否具有带特定签名的静态方法).
Jam*_*ran 15
我猜是近视.
最初设计时,接口仅用于与类的实例一起使用
IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();
Run Code Online (Sandbox Code Playgroud)
只有在引入接口作为泛型的约束时,向接口添加静态方法才具有实际用途.
(回应评论:)我认为现在更改它需要更改CLR,这将导致与现有程序集不兼容.
Joh*_*aft 14
接口指定对象的行为.
静态方法不指定对象的行为,而是指定以某种方式影响对象的行为.
Geo*_*rge 14
在接口表示"契约"的范围内,静态类实现接口似乎安静合理.
上述论点似乎都错过了关于合同的这一点.
因为接口的目的是允许多态,能够传递任何已定义的定义类的实例以实现定义的接口...保证在您的多态调用中,代码将能够找到你打电话的方法.允许静态方法实现接口是没有意义的,
你怎么称呼它?
public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
public static void MyMethod() { //Do Something; }
}
// inside of some other class ...
// How would you call the method on the interface ???
MyClass.MyMethod(); // this calls the method normally
// not through the interface...
// This next fails you can't cast a classname to a different type...
// Only instances can be Cast to a different type...
MyInterface myItf = MyClass as MyInterface;
Run Code Online (Sandbox Code Playgroud)
截至 2022 年中,当前版本的 C# 已完全支持所谓的static abstract成员:
interface INumber<T>
{
static abstract T Zero { get; }
}
struct Fraction : INumber<Fraction>
{
public static Fraction Zero { get; } = new Fraction();
public long Numerator;
public ulong Denominator;
....
}
Run Code Online (Sandbox Code Playgroud)
请注意,根据您的 Visual Studio 版本和安装的 .NET SDK,您必须至少更新其中之一(或可能同时更新两者),或者必须启用预览功能(请参阅使用预览功能)和 Visual Studio 中的预览语言)。
查看更多: