为什么实例方法不能作为静态方法调用

huangapple go评论76阅读模式
英文:

Why instance methods can't be called as static methods

问题

In Python, you can define a class method that accepts an instance as its first parameter, allowing you to call it as both a static method and an instance method. However, in .NET languages like C# and F#, this behavior is not supported by default. You need to explicitly use extension methods or workarounds to achieve similar functionality.

在Python中,您可以定义一个类方法,它接受一个实例作为其第一个参数,从而允许您将其既用作静态方法又用作实例方法。但是,在.NET语言(如C#和F#)中,默认情况下不支持此行为。您需要明确使用扩展方法或解决方法来实现类似的功能。

To answer your question:
回答您的问题:

The compiler in .NET languages doesn't automatically transform a static-style call into an instance-style call because it follows different language design principles. In Python, dynamic dispatch and duck typing are more prevalent, allowing such behavior. In contrast, .NET languages rely on more static typing and method resolution at compile-time.

.NET语言中的编译器不会自动将静态样式的调用转换为实例样式的调用,因为它遵循不同的语言设计原则。在Python中,动态分派和鸭子类型更为普遍,允许这种行为。相反,.NET语言依赖于更多的静态类型和在编译时解析方法。

To achieve a similar behavior in .NET languages, you can use extension methods, as you've shown in your code examples. However, this may require using lambdas or other workarounds for partial application.

要在.NET语言中实现类似的行为,您可以使用扩展方法,正如您在示例代码中所示。但是,这可能需要使用lambda或其他部分应用的解决方法。

If you have any further questions or need more clarification, please let me know.
如果您有进一步的问题或需要更多解释,请告诉我。

英文:

In python I can write code like this:

class MyClass:
    def foo(self, bar):
        return str(self) + bar

    def __str__(self):
        return "MyClass"


a = MyClass()
r1 = a.foo('1') # MyClass1
r2 = MyClass.foo(a, '1') # MyClass1

Which will define MyClass and allows me to use foo as static method, passing instance as first parameter. It can be performed because of high dynamism of language.

But why I can't use same principle in .Net languages?

type MyClass() =
    member this.Foo bar = this.ToString() + bar
    override _.ToString() = "MyClass"

let a = MyClass()
let r1 = a.Foo "1" // MyClass1
let r2 = MyClass.Foo a "1" // Compilation error
class MyClass
{
    public string Foo(string bar) => this.ToString() + bar;
    public override string ToString() => "MyClass";
}
class Program
{
    static void Main(string[] args)
    {
        var a = new MyClass();
        var r1 = a.Foo("1"); // MyClass1
        var r2 = MyClass.Foo(a, "1"); // Compilation error
    }
}

this implicitly passed as first parameter to any instance method during compilation and from perspective of compiler call a.Foo(b) is equivalent to MyClass(a, b).

Lack of this feature causes to write additional code to help type inference. So instead of writing this:

let startsWithAny str (chars: char[]) =
    chars.Any (String.StartsWith str)

I must write this:

let startsWithAny (str: string) (chars: char[]) =
    chars.Any (fun c -> str.StartsWith c)

or this:

let startsWithAny str (chars: char[]) =
    chars.Any (fun c -> (str :> string).StartsWith c)

Update

Due to some misunderstanding I'm extending my question.

At the moment I'm mostly write on F# and trying to take advantage of each feature include currying, partial application, etc.

Functional style gives such advantages as type inference, clarity, composability. So to use all of them, needs to remove all code that behaves unpredictably (virtual method call, overloads)

I want to write code as this:

let startsWithAny str (chars: char[]) =
    chars.Any (String.StartsWith str)
    // or better
    // Array.any chars (String.StartsWith str) // ideal world

As @DmitriTsoy said, it can be achieved with extension methods:

type System.String with
     static member StartsWith((str: string), (char: char)) =
         str.StartsWith char

But such extension makes it impossible to use partial application and results in resort of using lambdas fun c -> str.StartsWith c

Another way to implement extension:

type System.String with
     static member StartsWith (str: string) =
        Func<char,bool>(str.StartsWith)

It takes string and returns partially applied StartsWith. This leads to weird C# call:

chars.Any(c => String.StartsWith("someStr")(c))

but nice F#:

chars.Any (String.StartsWith str)

Question: Why compiler can't transform such static-style call to instance-style? If no static method found, try to find instance method and convert call to instance-style at compile time

答案1

得分: 4

简单的答案是,C# 是这样设计的。正如你所说,下面的代码不会编译,因为 Foo 不是静态的,而且签名也不存在:

var r2 = MyClass.Foo(a, "1"); // 编译错误,签名 Foo(MyClass, string) 不存在

如果我们有一些语法糖可以允许这样做,编译器将不得不为每个实例方法添加一个具有这样签名的静态方法:

Foo(MyClass, string) // 添加一个类的对象作为第一个参数

但请注意,在 C# 中,一个类不能同时拥有相同签名的静态方法和实例方法。

那么,作为一个程序员,如果我想编写一个接受同一类的另一个对象(与 this 不同)的 Foo 实例方法,该怎么办呢?我不能创建这样一个类:

class MySpecialArray
{
    int[] myArray;

    void Copy(int number) { /* 在 myArray 中复制 number */ }
    
    void Copy(MySpecialArray copied, int number) { /* 复制 copied 的值并在 myArray 中加上 number 的深拷贝 */ }
}
英文:

The simple answer is that C# was designed like this. As you say the code below does not compile because Foo is not static but also because the signature does not exist:

var r2 = MyClass.Foo(a, "1"); // Compilation error, the signature Foo(MyClass, string) does not exist

If we had some syntactic sugar that allowed it, the compiler would have to add, for each instance method, a static method with such a signature:

Foo(MyClass, string) // add an object of the class as a first parameter

But note that in C# a class can't have a static and an instance method with the same signature.

So what if, as a programmer, I want to write a Foo instance method that takes another object of the same class and different than this? I could not have a class such as:

class MySpecialArray
{
	int[] myArray;

	void Copy(int number) { /* copy number everywhere in myArray */ }
	
	void Copy(MySpecialArray copied, int number) { /* deep copy of copied's values + number in in myArray */ }
}

huangapple
  • 本文由 发表于 2020年1月6日 17:35:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609673.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定