C # виртуальный статический метод - PullRequest
31 голосов
/ 07 августа 2009

Почему статическое виртуальное невозможно? Зависит ли C # или просто не имеет никакого смысла в мире ОО?

Я знаю, что концепция уже подчеркнута, но я не нашел простого ответа на предыдущий вопрос.

Ответы [ 9 ]

51 голосов
/ 07 августа 2009

virtual означает, что вызываемый метод будет выбран во время выполнения в зависимости от динамического типа объекта. static означает, что для вызова метода не требуется никаких объектов.

Как вы предлагаете сделать оба в одном методе?

21 голосов
/ 07 августа 2009

У Эрика Липперта есть запись в блоге на эту тему, и, как обычно, с его сообщениями, он очень подробно освещает эту тему:

http://blogs.msdn.com/b/ericlippert/archive/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one.aspx

«Виртуальный» и «статический» противоположны! «Виртуальный» означает «определить метод, который будет вызван на основе информации о типе времени выполнения», а «статический» означает «определить метод, который должен быть вызван исключительно на основе статического анализа во время компиляции»

7 голосов
/ 12 декабря 2011

Ребята, которые говорят, что нет смысла в статических виртуальных методах. Если вы не понимаете, как это могло быть возможно, это не значит, что это невозможно. Есть языки, которые позволяют это !! Посмотрите на Delphi, например.

7 голосов
/ 13 сентября 2011

Противоречие между «статическим» и «виртуальным» является только проблемой c #. Если бы «static» был заменен на «class level», как во многих других языках, никто бы не был с завязанными глазами.

Жаль, что выбор слов сделал урезанный в этом отношении c #. Можно по-прежнему вызывать метод Type.InvokeMember для имитации вызова виртуального метода на уровне класса. Вам просто нужно передать имя метода в виде строки. Нет проверки времени компиляции, нет строгой типизации и нет контроля, что подклассы реализуют метод.

Немного красоты Delphi:

type
  TFormClass = class of TForm;
var
  formClass: TFormClass;
  myForm: TForm;
begin
  ...
  formClass = GetAnyFormClassYouWouldLike;
  myForm = formClass.Create(nil);
  myForm.Show;
end
5 голосов
/ 07 августа 2009

Я собираюсь быть тем, кто скептичен. То, что вы описываете, технически не является частью языка. Сожалею. Но возможно смоделировать его на языке.

Давайте рассмотрим то, что вы просите - вам нужен набор методов, которые не привязаны к какому-либо конкретному объекту, которые можно легко вызывать и заменять во время выполнения или компиляции.

Для меня это звучит так, как будто вам действительно нужен одноэлементный объект с делегированными методами.

Давайте соберем пример:

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

Учитывая все это, у нас теперь есть одноэлементный класс, который записывает значения валют, и я могу изменить его поведение. Я в основном определил поведение Convention во время компиляции и теперь могу изменить поведение во время компиляции (в конструкторе) или во время выполнения, что, я полагаю, является эффектом, который вы пытаетесь получить. Если вы хотите наследовать поведение, вы можете сделать это путем реализации обратного связывания (т. Е. Заставить новый метод вызывать предыдущий).

Тем не менее, я не особенно рекомендую приведенный выше пример кода. С одной стороны, это не потокобезопасно, и на самом деле не так много места, чтобы сохранить жизнь в здравом уме. Глобальная зависимость от такого рода структур означает глобальную нестабильность. Это один из многих способов, которыми изменяемое поведение было реализовано в смутные мрачные дни структур C: указателей на функции, и в данном случае единой глобальной структуры.

2 голосов
/ 15 декабря 2014

Да, это возможно.

Наиболее востребованный вариант использования для этого - иметь фабрики , которые можно "переопределить"

Для этого вам придется полагаться на параметры универсального типа, используя F-ограниченный полиморфизм .

Пример 1 Давайте возьмем фабричный пример:

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. Или вы можете захотеть, чтобы статические функции A были библиотекой, которая должна быть расширена за счет 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);
2 голосов
/ 07 августа 2009

Хотя технически невозможно определить статический виртуальный метод , по всем причинам, уже указанным здесь, вы можете функционально выполнить то, что я считаю, пытаясь использовать методы расширения C #.

Из MSDN:

Методы расширения позволяют вам «добавить» методы к существующим типам без создание нового производного типа, перекомпиляция или иное изменение оригинальный тип.

Проверьте Методы расширения C # (Руководство по программированию в C #) для получения более подробной информации.

1 голос
/ 07 августа 2009

В .NET диспетчеризация виртуального метода (приблизительно) осуществляется путем просмотра фактического типа объекта при вызове метода во время выполнения и поиска наиболее переопределенного метода из vtable класса. При вызове статического класса нет экземпляра объекта, который нужно проверить, и, следовательно, нет vtable для выполнения поиска.

0 голосов
/ 10 февраля 2015

Подведем итог всех представленных вариантов:

...