Почему методы экземпляра нельзя вызывать как методы stati c - PullRequest
1 голос
/ 06 января 2020

В python я могу написать код, подобный этому:

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

, который определит MyClass и позволит мне использовать foo в качестве метода stati c, передавая экземпляр в качестве первого параметра. Это может быть выполнено из-за высокой динамичности языка.

Но почему я не могу использовать тот же принцип в. Net языках?

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 неявно передано в качестве первого параметра для любого экземпляра метода во время компиляции и с точки зрения вызова компилятора a.Foo(b) эквивалентно MyClass(a, b).

Отсутствие этой функции приводит к написанию дополнительного кода, помогающего выводу типа. Поэтому вместо того, чтобы писать это:

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

Я должен написать это:

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

или это:

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

Обновление

В связи с некоторые недоразумения я расширяю свой вопрос.

В настоящее время я в основном пишу на F # и пытаюсь использовать все преимущества, включая карринг, частичное применение и т. д. c.

Функциональный стиль дает такие преимущества, как вывод типа, ясность, сочетаемость. Таким образом, чтобы использовать их все, необходимо удалить весь код, который ведет себя непредсказуемо (вызов виртуального метода, перегрузки)

Я хочу написать код так:

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

Как сказал @DmitriTsoy, это может быть достигнуто с помощью методов расширения:

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

Но такое расширение делает невозможным использование частичного приложения и приводит к использованию лямбда-символов fun c -> str.StartsWith c

Другой способ реализации расширения:

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

Он принимает строку и возвращает частично примененный StartsWith. Это приводит к странному C# вызову:

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

, но приятному F #:

chars.Any (String.StartsWith str)

Вопрос : почему компилятор не может преобразовать такие stati c стиль вызов стиль экземпляра ? Если метод stati c не найден, попробуйте найти метод экземпляра и преобразовать вызов в instance-style во время компиляции

1 Ответ

4 голосов
/ 06 января 2020

Простой ответ заключается в том, что C# был разработан так. Как вы говорите, приведенный ниже код не компилируется, потому что Foo не является статичным c , а , а также потому, что подпись не существует:

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

Если бы у нас было несколько синтаксисов c сахар, который позволил это, компилятор должен был бы добавить для каждого метода экземпляра метод stati c с такой сигнатурой:

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

Но обратите внимание, что в C# класс не может иметь stati c и метод экземпляра с той же сигнатурой.

Так что если я, как программист, хочу написать метод экземпляра Foo, который принимает другой объект того же класса и отличается от this? Я не мог иметь такой класс, как:

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 */ }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...