相同名称的静态方法和实例方法有什么区别?

69

我在C#中有一个同时具有静态和非静态接口的类。在一个类中是否可能拥有相同名称和签名的静态和非静态方法?

当我尝试这样做时,编译器会报错,但是由于某种原因,我认为有一种方法可以实现这个。我错了吗?或者在同一个类中拥有静态和非静态方法是不可能的吗?

如果无法实现,是否有一种通用方法可以实现这样的需求?

编辑
从我收到的回复中,很明显这是不可能的。我将采用不同的命名系统来解决这个问题。


你能提供一个你想要这样做的例子吗? - Eric Schoonover
3
我不建议你依赖大小写区分两种方法,这是一个非常糟糕的想法。你不应该拥有只有大小写不同的两个方法:我保证你最终会调用到错误的方法。 - Euro Micelli
4
将方法重命名为不同的名称。仅依赖大小写不安全,特别是如果编译后的代码有机会被其他.NET语言使用,并且这些语言对大小写不敏感。不能仅仅因为该语言对大小写敏感而依赖于这一事实来消除方法名称歧义。 - Scott Dorman
这段代码只会在C#中编译。我的公司专门使用C#,并且不可能将代码转换或用于其他语言,所以在这种情况下我是安全的。 - Dan Herbert
1
了解Ball vs BaII或LOOP vs L00P之间的区别已经足够麻烦了,而了解doSomething vs DoSomething之间的区别则更加棘手。在你意识到之前,你会发现自己需要对每种情况进行注释,以便其他程序员或者下周再次查看时能够清晰明了。 - Abel
1
记录一下,你不能使用扩展方法来取代非静态接口。 - yoyo
8个回答

68

不可以。限制的原因是静态方法也可以在非静态上下文中调用,而无需在方法名称前加上类名(例如,使用 MyStaticMethod() 而不是 MyClass.MyStaticMethod())。如果同时存在静态和非静态方法,则编译器无法确定你要调用哪一个。

您可以拥有相同名称但具有不同参数的静态和非静态方法,遵循方法重载的相同规则,但它们不能具有完全相同的签名。


36
我认为这个答案的逻辑有缺陷。我不明白为什么在这种情况下C#不能通过要求使用“this.”来调用实例方法来执行消歧义。此外,andasa展示了静态和实例方法可以拥有相同的名称。 - mheyman
4
如果出现冲突,他们可能可以进行某种检查以默认选择静态方法,并要求这样做来访问实例方法。当然,在处理静态属性时,这会变得棘手,因为在方法的范围内,如果您的参数与一个静态/实例属性同名,则必须使用类名指定静态版本,使用this指定实例版本,如果没有,则访问该参数。对我来说,这听起来像是引入了大量难以找到的错误的绝佳方式。 - ckramer
6
我认为所有来自非静态作用域的调用默认应该指向非静态方法。如果您需要全局变量,您应该通过在前缀中加上类名来请求它。 - Nick Sotiros

62

实际上,通过显式实现一个接口,有一种方法可以实现这个目的。这并不是完美的解决方案,但在某些情况下可以起作用。

interface IFoo
{
    void Bar();
}

class Foo : IFoo
{
    static void Bar()
    {
    }

    void IFoo.Bar()
    {
        Bar();
    }
}

有时候我在为P/Invoke调用编写包装类时会遇到这种情况。


我这样做的确切原因是P/Invoke调用。谢谢 :) - Asheh
2
刚刚修复了,@Kamarey。显式接口实现不能有访问修饰符,它总是公共的。 - Palec
1
当我在我的应用程序中执行(new Foo()).Bar();时,出现错误:成员“Foo.Bar()”无法通过实例引用访问;请改为使用类型名称。 - T.Todua
1
是的,你需要将其引用为IFoo才能这样做。你可以使用((IFoo)(new Foo())).Bar();或者IFoo foo = new Foo(); foo.Bar(); - Richard

12

您可以在实例方法中调用静态方法而无需指定类型名称:

class Foo
{
    static void Bar()
    {
    }

    void Fizz()
    {
        Bar();
    }
}

……因此,有一个静态方法和一个实例方法使用相同的签名是不允许的,这是有道理的。

您想要实现什么?如果不知道具体情况,很难建议解决方法。我只会将其中一个方法重命名。


5

C#在这方面设计不够好...

虽然你可能想要全局或非全局的,但它应该默认选择一个,如果你想要另一个,那么你只需更加限定。

class Logger {
   public static Logger instance;

   public static void Log(string message) {
       instance.Log(message); // currently the compiler thinks this is ambiguous, but really its not at all.  Clearly we want the non-static method
   }

   public void Log(string message) {

   }

   public void DoStuff() {
      Log("doing instance stuff"); // this could be ambiguous, but in my opinion it should default to a call to this.Log()
      Logger.Log("doing global stuff"); // if you want the global qualify it explicitly
   }
}

1

好的。这个问题的根源在于C#不应该让你在没有指定类型名称的情况下从实例方法中调用静态方法。

其他完全面向对象的语言(如Smalltalk)不允许这样做,对于理解对象的人来说也只会造成困惑。 实例和类(或静态)之间的分离非常重要,拥有一种促进这些细节混淆的语言......不是一个好主意......但这是我们从微软公司期望的类型的东西。

Adrian


1
您的帖子并没有回答问题。请考虑添加具体的答案或者在评论中解释问题。 - Seeta Somagani
2
我理解为什么会有人给这篇回答点踩,因为它并没有回答问题,但这确实是问题的原因。对于开发者来说,在静态方法之前看到类名作为第一眼指示符更有意义,表明“是的,这是一个静态方法、属性、字段”等等。 - Brandon
3
三年后,我想说我欣赏@Adrian提出的观点,但不包括那些完全错误的贬低性语句,特别是:“C# 不应该让你调用”,“这是我们从微软所期望的典型东西。”虽然第一个观点是可以持有的好意见,但不应当作为事实陈述。 - Nicholas Petersen
1
那些想在C#中实践良好的对象架构和设计的人们注定会遭受痛苦。正是这样的小限制增加了复杂性并降低了清晰度。类与实例不同,两者都应该能够声明一个“Foo”而不会混淆。在实例方法中指定Foo而不是Klass.Foo的方便性优先于正确性,但远远被语言本身的冗长所压倒。不适当地优化语言是C++谱系悲惨历史的一部分。 - The Software Barbarian

0

你可以有相同名称的静态方法和实例方法,只要它们在参数的数量或类型上有所不同。这与您如何在类中拥有两个具有相同名称的实例方法是相同的规则。

尽管从技术上讲,在静态方法 vs 实例方法的情况下,它们已经通过实例方法中隐式的 this 参数存在差异,但该差异不足以让编译器确定您想要调用其中哪一个。

更新:我犯了一个错误。返回值不足以具有不同的签名。


1
错误: 只要它们的参数数量和/或类型不同,就不会重载返回类型。 - asterite

0
你可以添加一个可选参数
public class Logger
{
    public static Logger logger = new Logger();

    public void Log(string? message)
    {
        Console.WriteLine(message);
    }

    public void LogStatic(string? message)
    {
        Log(message, null);
    }

    public static void Log(string? message, object? _ = null)
    {
        logger.Log(message);
    }
}

.

static void Main(string[] args)
{
    Logger.logger.Log("...");
    Logger.logger.LogStatic("...");
    Logger.Log("...");
}

虽然从技术上讲,这个可选默认参数的方法可以工作,但这是一个相当巧妙的解决方案,可能会导致歧义(Logger.logger.Log("..."); 到底调用了哪个方法?),而且代码可读性较差,因为实际行为取决于了解编译器如何解析方法调用。这是一个相当聪明的解决方案,但我不建议在生产代码中使用。 - undefined

-1
可以使用扩展方法来实现:
public class Foo
{
    public static void DoSomething(int parameter)
    {
        Console.WriteLine(parameter.ToString());
    }
}
public static class FooExtensions
{
    public static void DoSomething(this Foo foo, int parameter)
    {
        Foo.DoSomething(parameter);
    }
}

如果他们没有使用包含您的扩展方法的命名空间,那么它就无法工作。但这对于扩展方法总是成立的。所以它并不完美。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接