Возможно ли определить метод оператора расширения?

Можно ли определить метод расширения, который в то же время является оператором? Я хочу, чтобы фиксированный класс добавлял возможность использовать известный оператор, который фактически не может быть применен. Для этого конкретного случая я хочу сделать это:

   somestring++;  //i really know that this string contains a numeric value

И я не хочу распространять типы конверсий для всего кода. Я знаю, что я мог бы создать класс-оболочку над строкой и определить этот оператор, но я хочу знать, возможно ли подобное, чтобы избежать поиска и замены каждого объявления строки с помощью MySpecialString.

Отредактировано: поскольку большинство из них говорят, что строка запечатана, поэтому вывод невозможен, поэтому я изменяю "полученный" на "обертку", свою ошибку.

Ответ 1

Нет, это невозможно сделать из-за пределов класса. Оператор ++ должен быть определен внутри класса, который увеличивается. Вы можете создать свой собственный класс, который будет конвертирован из строки и будет иметь перегрузку ++, или вы можете забыть об этой идее и использовать обычные методы.

Ответ 2

Это невозможно в С#, но почему не стандартный метод расширения?

 public static class StringExtensions {
     public static string Increment(this string s) {
          ....
     }
 }

Я думаю, что somestring.Increment() еще более читабельна, так как вы не путаете людей, которые действительно не ожидали увидеть ++ применительно к строке.

Ответ 3

Ярким примером того, где это было бы полезно, является возможность расширения класса TimeSpan для включения * и/операторов.

Это то, что идеально работает...

public static class TimeSpanHelper
{
    public static TimeSpan operator *(TimeSpan span, double factor)
    {
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds * factor);
    }

    public static TimeSpan operator *(double factor, TimeSpan span)  // * is commutative
    {
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds * factor);
    }

    public static TimeSpan operator /(TimeSpan span, double sections)
    {
        return TimeSpan.FromMilliseconds(span.TotalMilliseconds / factor);
    }

    public static double operator /(TimeSpan span, TimeSpan period)
    {
        return span.TotalMilliseconds / period.TotalMilliseconds);
    }

}

Ответ 4

Нет, у вас не может быть метода расширения, который также является оператором. Методы расширения могут быть объявлены только в статических классах, которые не могут иметь экземпляры и в соответствии с спецификацией С#,

Пользовательские декларации операторов всегда требуют, чтобы хотя бы один из параметров имел тип класса или структуры, содержащий декларацию оператора. [7.3.2]

Следовательно, метод расширения также не может быть перегруженным оператором.

Кроме того, вы не можете переопределить System.String, так как это запечатанный класс.

Ответ 5

Строковый класс запечатан в С#, поэтому создание производного по строкам класса фактически невозможно.

Говоря, что метод расширения, конечно, будет работать очень хорошо (как и стандартный статический метод в классе-помощнике), но он не будет оператором, просто обычным методом.

Ответ 6

В настоящее время это не поддерживается, потому что методы расширения определены в отдельном статическом классе, а статические классы не могут иметь переопределения операторов.

Ответ 7

Это все правда, но было бы неплохо, если бы M $добавила эту функциональность в будущем. Иногда в структуре просто отсутствуют вещи, и расширение может помочь подключить разрыв (или устранить проблему), иногда это могут быть операторы.

Пример. Чтобы сравнить IP-адреса, вы должны использовать метод Equals для прямого сравнения (конечно, части структуры также можно сравнить, как и байты адреса отдельно, но это другая история). Однако использование оператора == всегда возвращает false на уровне объекта (т.е. Не преобразовывая их в строки и т.д.). Как трудно помещать вызов метода Equals внутри вызова оператора == (это риторический), но мы не можем этого сделать. Это непоследовательно и место для ошибок в ползучести (обратите внимание, что это не подводит, просто всегда приравнивается к false, а Equals - нет).

Ответ 8

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

//i really know that this string contains a numeric value

- это именно та ситуация, в которой была изобретена безопасность типа.

Другой способ взглянуть на это состоит в том, что, написав этот оператор, вы нарушили множество других функций и операторов, которые работают с классом string, поскольку они не обязательно сохраняют свойство содержать числовое значение. Используя класс-оболочку, а не производный класс, вы только повторно реализуете те функции string, которые имеют смысл для числовых строк.

Ответ 9

i был в очень похожей ситуации, описанной вами: мне нужно было увеличить текст (с уверенным числовым значением) в текстовом поле Windows Forms.

Я понимаю вашу потребность, как вы описали

SomeString++;//Я действительно знаю, что эта строка содержит числовое значение

Моя душа - это что-то вроде того, что, по моему мнению, близко к вашему описанию

somestring = (incrementable) somestring + 1

Все, что мне нужно было сделать, это

  • создание класса incrementable
  • определяющий в нем явный оператор (чтобы преобразовать string в incrementable)
  • определение неявного оператора в нем (чтобы преобразовать incrementable обратно в string)
  • для + (знак плюс)

Вот как мой класс выглядит полностью

public class incrementable
{
    public string s; // For storing string value that holds the number

    public incrementable(string _s)
    {
        s = _s;
    }

    public static explicit operator incrementable(string tmp)
    {
        return new incrementable(tmp);
    }

    public static implicit operator string(incrementable tmp)
    {
        return tmp.s;
    }

    public static incrementable operator +(incrementable str, int inc) // This will work flawlessly like `somestring = (incrementable)somestring + 1`
        => new incrementable((Convert.ToInt32(str.s) + inc).ToString());

    public static incrementable operator ++(incrementable str) // Unfortunately won't work, see below
        => new incrementable((Convert.ToInt32(str.s) + 1).ToString());
}

К сожалению, мне просто не удалось улучшить мой класс с помощью унарного оператора ++. Причиной против использования неявного преобразования, такого как ((incrementable)somestring)++, является то, что он приведет к ошибке, говоря The operand of an increment or decrement operator must be a variable, property or indexer, следовательно, не может быть результатом этого каста.

В любом случае, надеюсь, что это поможет!

Ответ 10

Как показано в других ответах, это нельзя сделать напрямую. Но что, если вам это нужно, скажем, вы хотите улучшить StringBuilder как

void Main()
{
    var log = (StringBuilder)"Hello ";
    log += "World!";
    log += "\nThis example shows how to extend StringBuilder";
    log.ToString().Dump();
}

как этого добиться (т.е. использовать оператор + вместо sb.Append(str);)?


Ответ: В этом случае вы не можете сделать это напрямую, но вы можете сделать следующее:

Запустите его в DotNetFiddle

void Main()
{
    var log = (StrBuilder)"Hello "; // same as: "Hello ".ToStrBuilder();
    log += "World!";
    log += "\nThis example shows how to extend StringBuilder";
    log.ToString().Dump();
}

public static class Extensions
{
    public static StrBuilder ToStrBuilder(this string str)
    {
        return new StrBuilder(str);
    }   
}

public class StrBuilder
{
    private StringBuilder sb;

    public StrBuilder()
    {
        sb = new StringBuilder();
    }

    public StrBuilder(string strB)
    {
        sb = new StringBuilder(strB);
    }

    public static implicit operator StrBuilder(string self)
    {
        return new StrBuilder(self);
    }

    public static StrBuilder operator +(StrBuilder sbA, string strB)
    {       
        return sbA.Append(strB);
    }

    public StrBuilder Append(string strB)
    {
        sb.Append(strB);
        return this;
    }

    public override string ToString()
    {
        return sb.ToString();
    }
}

Примечание: Вы не можете наследовать от StringBuilder, потому что это запечатанный класс, но вы можете написать класс, который "упаковывает" StringBuilder, то есть, что здесь делается (благодаря ответу IanNorton относительно неявного преобразования).