Я работал над созданием своего собственного языка со статической типизацией, когда понял, что может оказаться невозможным различить выражение, использующее оператор <
, и аргумент типа для класса или метода.
Основная причина этого заключается в том, что, как и в C#, не все классы должны быть предварительно объявлены перед использованием, поэтому, когда идентификатор анализируется после <
, это может быть либо выражение типа valueA < valueB
или это может быть аргумент типа valueA<valueB>
.
Тогда я подумал, может быть, если есть закрывающий >
, то его можно проанализировать как аргумент типа, но потом я вспомнил, что хотел, чтобы мой язык имел перегрузку операторов, поэтому выражения вроде valueA < valueB > (valueC)
могли быть совершенно действительный.
Я решил поэкспериментировать с другими языками и обнаружил, что C# больше всего похож на язык, который я пытался создать, и, возможно, я сломал его.
Выражение foo < bar > (2)
в приведенном ниже коде должно быть абсолютно правильным выражением из-за класса с перегруженными операторами <
и >
.
Насколько мне известно, выражение должно быть проанализировано как (foo
, но вместо этого я получаю сообщение об ошибке, указывающее, что "Переменная 'foo' не может использоваться с аргументами типа ."
Чтобы доказать, что это выражение должно быть допустимым, я поменял местами знаки <
и >
, чтобы выражение выглядело как < code>foo > bar < (2), и эта программа скомпилировала и распечатала именно то, что вы ожидаете, MainClass+baz
.
В этом конкретном примере компилятор, вероятно, мог бы понять это, потому что он знает, что foo
является переменной, и может предположить, что <
означает выражение, но если бы foo был статическим членом другого класса, то было бы невозможно различить выражение <
и аргумент типа.
using System;
class MainClass {
public static void Main (string[] args) {
baz foo = new baz();
baz bar = new baz();
// perfectly valid expression, results in error: The variable `foo' cannot be used with type arguments
Console.WriteLine(foo < bar > (2));
}
class baz {
public static baz operator<(baz l, baz r) {
return l;
}
public static baz operator>(baz l, baz r) {
return l;
}
public static baz operator<(baz l, int r) {
return l;
}
public static baz operator>(baz l, int r) {
return l;
}
}
public String toString() {
return "baz";
}
}
Мой вопрос в том, как решить эту проблему в компиляторе С# и других языках?
Я вижу несколько вариантов:
- Сообщить об ошибке в неоднозначной ситуации
- Может быть, есть еще какой-то контекст, который можно использовать для разрешения этой двусмысленности.
- C# существенно сломан, и нам следует разработать новый синтаксис для аргументов типа в будущих языках. Если да, то как это должно выглядеть?
Может быть, есть какой-то стандарт на этот счет, но я думаю, что принять это было бы просто небрежностью, и мы должны, по крайней мере, выбрать вариант 1.
1 ответ
foo
- локальная переменная, но вы пытаетесь использовать ее как имя типа класса...
Следовательно, это вовсе не совершенно правильное выражение.
Вы не можете написать:
baz foo = new baz();
baz bar = new baz();
Console.WriteLine(foo<bar>(2));
Но вы можете написать:
var instance = baz<int>(2);
Если у вас есть:
class baz<T>
{
}
Здесь вы пытаетесь создать экземпляр класса baz с параметром универсального типа int без использования ключевого слова new
, или, возможно, это может быть неправильно написанное приведение, но компилятор не может этого знать.
Когда вы определяете класс baz<T>
, это то, что называется открытым универсальным типом.
И когда вы создаете экземпляр baz<int>
, это так называемый закрытый сконструированный универсальный тип.
Вы пишете приведенный ниже код должен быть абсолютно корректным выражением из-за класса с перегруженными операторами < и >...
Но компилятор не может интерпретировать val1 < val2 > (val3)
как val1 < val2 && val2 > (val3)
, как с математическим правилом, или как (val1 < val2) > (val3)
, как вы ожидаете, потому что оператор <>()
имеет приоритет над <
и >
.
Компилятор думает, что вы пытаетесь использовать baz of T
, поэтому компилятор выводит ошибку, потому что компилятор не может выбрать между двумя случаями, между <>()
и <
next >
.
Таким образом, компилятор интерпретирует это как Type<Type>(parameter)
и ничего больше.
Похожие вопросы
Новые вопросы
c#
C# (произносится как «see Sharp») — это высокоуровневый мультипарадигменный язык программирования со статической типизацией, разработанный Microsoft. Код C# обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, которое включает в себя .NET, .NET Framework, .NET MAUI и Xamarin среди прочих. Используйте этот тег для ответов на вопросы о коде, написанном на C#, или о формальной спецификации C#.
<
и>
, чтобы они возвращали базовые объекты, а не логические значения, поэтому ваша аналогия2 > 1 < (3)
оценивается какtrue > 3
не применима здесь. Выражение в приведенном выше кодеfoo > bar < (2)
будет оценено как(baz obj) < (2)
, которое затем будет оценено как(baz obj)
. Я даже сказал, что оно компилируется, потому что оно компилируется, и, поскольку обращениеfoo < bar > (2)
будет иметь тот же порядок старшинства, оно также должно быть допустимым выражением. Какой из трех вариантов выбрать?(foo<bar)>(2)
илиfoo < bar > 2
, потому что оператор<>()
имеет приоритет.>
, и если вы прочтете мой вопрос, то увидите, что я хочу знать, должен ли C# сообщать об этом как об ошибке, и как я могу избежать этой проблемы в мой собственный язык, который я разрабатываю. Я четко и подробно описал, что знаю, в чем проблема.<>()
не имеет приоритета, просто<>
, ваш второй пример не скомпилируется, а первый будет. Это было моей ошибкой за то, что я включил ненужный()
вокруг 2 в моем вопросе.