почему я не могу быть компактным с желаемым полиморфизмом C #? - PullRequest
5 голосов
/ 23 октября 2009

Вот что я хочу сделать:

XmlWriter writer = XmlWriter.Create(
    (string.IsNullOrEmpty(outfile) ? Console.Out : outfile)
);

Это не компилируется, однако, выдает ошибку «Тип условного выражения не может быть определен, поскольку не существует неявного преобразования между« System.IO.TextWriter »и« string »». Приведенный выше код является упрощением следующего:

XmlWriter writer;

if (string.IsNullOrEmpty(outfile))
{
    writer = XmlWriter.Create(Console.Out); // Constructor takes TextWriter
}
else
{
    writer = XmlWriter.Create(outfile); // Constructor takes string
}

Эти два вызова Create совершенно допустимы, и это компилируется. Есть ли способ сделать это более компактным, как я пытался сделать с помощью встроенного теста?

Мне не имеет смысла, что то, что я хочу, не работает. Мысленно обдумывая это, кажется, что компилятор оценит string.IsNullOrEmpty(outfile), чтобы определить, какой случай взять:

  • Если бы условие было истинным, оно пошло бы с Console.Out, а затем увидело, что ему нужно полиморфно выбрать версию XmlWriter.Create, которая принимает TextWriter.
  • Если бы условие было ложным, оно пошло бы с outfile, а затем увидело, что ему нужно полиморфно выбрать версию XmlWriter.Create, которая принимает строку.

Не исказило ли программирование в ML мой мозг?

Ответы [ 7 ]

18 голосов
/ 23 октября 2009

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

XmlWriter writer = String.IsNullOrEmpty(outfile)
    ? XmlWriter.Create(Console.Out)
    : XmlWriter.Create(outfile);
7 голосов
/ 23 октября 2009

Каждый, кажется, предлагает следующее:

XmlWriter writer = String.IsNullOrEmpty(outfile)
    ? XmlWriter.Create(Console.Out)
    : XmlWriter.Create(outfile);

Однако это также выполнимо:

XmlWriter writer = XmlWriter.Create(string.IsNullOrEmpty(outfile)
    ? Console.Out : new StreamWriter(outfile));

Последнее ближе к вашей первоначальной попытке и, IMO, более компактно.

3 голосов
/ 23 октября 2009

Компилятор C # выбирает метод для статического выполнения во время компиляции. IL, который генерируется при компиляции, является ссылкой на определенный метод. Часть полиморфизма появляется во время выполнения, когда она выбирает, какую реализацию этой конкретной функции выполнять.

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

Перейдите к этому, и оно будет работать.

XmlWriter writer = string.IsNullOrEmpty(outfile) ? 
    XmlWriter.Create(Console.Out) :
    XmlWriter.Create(outfile);
2 голосов
/ 23 октября 2009

Здесь происходит пара вещей.

Во-первых, «исключение» происходит из-за Тернарного Оператора (tm), а не из-за того, где вы его используете. Проблема в том, что у вас есть одно выражение, которое пытается вернуть два разных типа, которые не могут быть разрешены в один, общий базовый тип (отличный от объекта).

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

2 голосов
/ 23 октября 2009

Проблема в том, что вы не можете определить во время компиляции, что

(string.IsNullOrEmpty(outfile) ? Console.Out : outfile)

должен вернуться. Это будет строка или TextWriter? Это можно определить только во время выполнения, следовательно, ошибка компиляции, потому что? Оператор должен быть разрешен во время компиляции.

Лучшее, что вы можете из этого получить, вероятно, будет:

XmlWriter writer = string.IsNullOrEmpty(outfile)
    ? XmlWriter.Create(Console.Out)
    : XmlWriter.Create(outfile);
1 голос
/ 23 октября 2009

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

1 голос
/ 23 октября 2009

Нет, вы должны сделать два отдельных вызова, так как они являются двумя отдельными конструкторами.

Какая перегрузка для вызова определяется во время компиляции, поэтому вы не можете выбрать тип данных во время выполнения для вызова разных перегрузок.

Кроме того, условный оператор может возвращать только один тип данных, вы не можете заставить его возвращать разные типы данных в зависимости от выбора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...