В 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 во время компиляции