Почему статическое виртуальное невозможно? Является ли С# зависимым или просто не имеет никакого смысла в мире OO?
Я знаю, что концепция уже подчеркнута, но я не нашел простого ответа на предыдущий вопрос.
Почему статическое виртуальное невозможно? Является ли С# зависимым или просто не имеет никакого смысла в мире OO?
Я знаю, что концепция уже подчеркнута, но я не нашел простого ответа на предыдущий вопрос.
virtual
означает, что указанный метод будет выбран во время выполнения, в зависимости от динамического типа объекта. static
означает, что для вызова метода не требуется объект.
Как вы предлагаете делать оба в одном и том же методе?
У Эрика Липперта есть сообщение в блоге об этом, и, как обычно, с его постов, он охватывает тему на большой глубине:
"виртуальные" и "статические" - противоположности! "virtual" означает "определить метод, который будет вызываться на основе информации о типе времени выполнения", а "статический" означает "определить метод, который будет называться исключительно на основе статического анализа времени компиляции"
Противоречие между "статическим" и "виртуальным" - это только проблема С#. Если "статические" были заменены на "уровень класса", как и во многих других языках, никто не будет с завязанными глазами.
Слишком плохо выбор слов, сделанных С# калекой в этом отношении. По-прежнему можно вызвать метод Type.InvokeMember для имитации вызова уровня класса, виртуального метода. Вам просто нужно передать имя метода как строку. Отсутствие проверки времени компиляции, отсутствие сильного ввода и отсутствие контроля над тем, что подклассы реализуют этот метод.
Некоторая красота Дельфи:
type
TFormClass = class of TForm;
var
formClass: TFormClass;
myForm: TForm;
begin
...
formClass = GetAnyFormClassYouWouldLike;
myForm = formClass.Create(nil);
myForm.Show;
end
Ребята, которые говорят, что нет смысла в статических виртуальных методах. Если вы не понимаете, как это возможно, это не означает, что это невозможно. Есть языки, которые позволяют это! Посмотрите, например, на Delphi.
Я собираюсь быть тем, кто на днях. То, что вы описываете, технически не является частью языка. Сожалею. Но его можно смоделировать в пределах языка.
Давайте рассмотрим то, о чем вы просите, - вам нужен набор методов, которые не привязаны к какому-либо конкретному объекту, который может быть легко вызван и заменен во время выполнения или времени компиляции.
Для меня это похоже на то, что вы действительно хотите - это одноэлементный объект с делегированными методами.
Сопоставьте пример:
public interface ICurrencyWriter {
string Write(int i);
string Write(float f);
}
public class DelegatedCurrencyWriter : ICurrencyWriter {
public DelegatedCurrencyWriter()
{
IntWriter = i => i.ToString();
FloatWriter = f => f.ToString();
}
public string Write(int i) { return IntWriter(i); }
public string Write(float f) { return FloatWriter(f); }
public Func<int, string> IntWriter { get; set; }
public Func<float, string> FloatWriter { get; set; }
}
public class SingletonCurrencyWriter {
public static DelegatedCurrencyWriter Writer {
get {
if (_writer == null)
_writer = new DelegatedCurrencyWriter();
return _writer;
}
}
}
в использовании:
Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400.0
SingletonCurrencyWriter.Writer.FloatWriter = f => String.Format("{0} bucks and {1} little pennies.", (int)f, (int)(f * 100));
Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400 bucks and 0 little pennies
Учитывая все это, теперь у нас есть одноэлементный класс, который выписывает валютные значения, и я могу изменить его поведение. Я в основном определил соглашение о поведении во время компиляции и теперь могу изменить поведение либо во время компиляции (в конструкторе), либо во время выполнения, что, я считаю, эффект, который вы пытаетесь получить. Если вы хотите наследовать поведение, вы можете сделать это путем реализации обратной цепочки (т.е. Чтобы новый метод вызывал предыдущий).
Тем не менее, я не особо рекомендую приведенный выше пример кода. Во-первых, он не является потокобезопасным, и на самом деле не так много, чтобы поддерживать жизнь. Глобальная зависимость от такого типа структуры означает глобальную нестабильность. Это один из многих способов, с помощью которых изменяемое поведение было реализовано в темные дни C: structs указателей функций, и в этом случае одна глобальная структура.
В .NET диспетчеризация виртуальных методов (грубо) выполняется путем просмотра фактического типа объекта при вызове метода во время выполнения и нахождения наиболее переопределяющего метода из класса vtable. При вызове статического класса нет экземпляра объекта для проверки, и поэтому нет vtable для поиска.
В то время как технически невозможно определить метод статический виртуальный, по всем причинам, уже указанным здесь, вы можете функционально выполнить то, что, как я думаю, пытается использовать методы расширения С#.
Из MSDN:
Методы расширения позволяют вам "добавлять", методов для существующих типов без создавая новый производный тип, перекомпилировать или иным образом модифицировать оригинальный тип.
Подробнее о С# Extension Methods (Руководство по программированию на С#).
Да, это возможно.
Наиболее востребованным вариантом для этого является наличие фабрик, которые могут быть "переопределены"
Для этого вам придется полагаться на параметры типового типа, используя F-ограниченный полиморфизм.
Пример 1 Возьмем пример factory:
class A: { public static A Create(int number) { return ... ;} }
class B: A { /* How to override the static Create method to return B? */}
Вы также хотите, чтобы createB
был доступен и возвращал объекты B в классе B. Или вам может потребоваться, чтобы статические функции были библиотекой, которая должна быть расширена B. Решение:
class A<T> where T: A<T> { public static T Create(int number) { return ...; } }
class B: A<B> { /* no create function */ }
B theb = B.Create(2); // Perfectly fine.
A thea = A.Create(0); // Here as well
Пример 2 (продвинутый): Пусть задает статическую функцию для умножения матриц значений.
public abstract class Value<T> where T : Value<T> {
//This method is static but by subclassing T we can use virtual methods.
public static Matrix<T> MultiplyMatrix(Matrix<T> m1, Matrix<T> m2) {
return // Code to multiply two matrices using add and multiply;
}
public abstract T multiply(T other);
public abstract T add(T other);
public abstract T opposed();
public T minus(T other) {
return this.add(other.opposed());
}
}
// Abstract override
public abstract class Number<T> : Value<T> where T: Number<T> {
protected double real;
/// Note: The use of MultiplyMatrix returns a Matrix of Number here.
public Matrix<T> timesVector(List<T> vector) {
return MultiplyMatrix(new Matrix<T>() {this as T}, new Matrix<T>(vector));
}
}
public class ComplexNumber : Number<ComplexNumber> {
protected double imag;
/// Note: The use of MultiplyMatrix returns a Matrix of ComplexNumber here.
}
Теперь вы также можете использовать статический метод MultiplyMatrix
для возврата матрицы комплексных чисел непосредственно из ComplexNumber
Matrix<ComplexNumber> result = ComplexNumber.MultiplyMatrix(matrix1, matrix2);
Подводя итог всем представленным опциям:
Это не часть С#, потому что в нем static
означает "не привязанный ни к чему во время выполнения" , как это было с C ( и, возможно, раньше). static
сущности привязаны к типу объявления (таким образом, могут обращаться к другим его объектам static
), но только во время компиляции.
static
(при необходимости вообще) означает "привязанный к типу объекта во время выполнения" . Примеры включают Delphi, Python, PHP.Это можно эмулировать несколькими способами, которые можно классифицировать как: