Здравствуйте, VladD2, Вы писали:
TB>>Только какой тип у этого x внутри блока? B или C?
VD>Наиболее частный общий.
Он может быть неоднозначным, например, у них могут быть несколько общих интерфейсов, не связанных друг с другом. Или, например, у контравариантных делегатов Action<Array> и Action<IList<object>> есть общий базовый тип MulticastDelegate, но он не самый специфичный. Потому что у них ещё есть более узкие общие базовые типы Action<string[]>, Action<string[][]> (и много других), но ни один из них не является более специфичным чем другой, хотя они все более специфичны, чем MulticastDelegate.
А по поводу твоего исходного вопроса — это можно сделать так:
if (foo() is (B or C) and var x)
{
// здесь x имеет тип A, но попадаем сюда, только если значение x имеет тип B или С (или более производный)
}
Или же можно написать так (это вопрос стиля):
if (foo() is var x and (B or C))
{
// здесь x имеет тип A, но попадаем сюда, только если значение x имеет тип B или С (или более производный)
}
А если, например, foo() имеет тип object, а ты хочешь чтобы x имела тип A (общий для B и C), то (ввиду возможной неоднозначности указанной выше) надо указать, какой именно общий тип ты хочешь у переменной:
if (foo() is (B or C) and A x)
{
// здесь x имеет тип A, но попадаем сюда, только если значение x имеет тип B или С (или более производный)
}
или, как вариант,
if (foo() is A x and (B or C))
{
// здесь x имеет тип A, но попадаем сюда, только если значение x имеет тип B или С (или более производный)
}
Что-то не понимаю. МС не позволяет сделать или-паттерн в паттер-матчинге, или я не пойму как это синтаксически записать?
Есть ситуация:
class A {}
class B : A {}
class C : A {}
class D : A {}
...
A foo() => new C();
if (foo() is B x)
{
// код использующий x
}
Я хочу сделать еще один паттерн:
if (foo() is B x || foo() is C x)
{
// код использующий x
}
но компилятор Щарп орет, что переменную x нельзя дважды объявлять. Это что же в Шарп не завезли или-патерн?
Другими словами, аналога вот такого немерлового кода написать нельзя?
module Program
{
class A {}
class B : A {}
class C : A {}
class D : A {}
Main() : void
{
def foo() : A { C() }
match (foo())
{
| B asx
| C asx => WriteLine(x) // в x экземпляр C или B, но не D и не A, а переменная имеет тип A
| _ => WriteLine("other")
}
}
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, T4r4sB, Вы писали:
TB>Немерле самый лучший язык, это понятно. Только какой тип у этого x внутри блока? B или C? Или комплиятор должен неявно продублировать тело ветки?
Наиболее частный общий. Т.е. если у нас есть некая иерархия — это будут типы наиболее вложенные в ней, но которые могут удовлетворить условию.
Скажем для данного примера это будет A. А если создать более ветвистую иерархию, например:
class A {}
class B : A {}
class C : B {}
class D : A {}
class E : B {}
то для E и C общим будет B, а для других сочетаний A.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>Другими словами, аналога вот такого немерлового кода написать нельзя? VD>
VD>module Program
VD>{
VD> class A {}
VD> class B : A {}
VD> class C : A {}
VD> class D : A {}
VD> Main() : void
VD> {
VD> def foo() : A { C() }
VD> match (foo())
VD> {
VD> | B asx
VD> | C asx => WriteLine(x) // в x экземпляр C или B, но не D и не A, а переменная имеет тип A
VD> | _ => WriteLine("other")
VD> }
VD> }
VD>}
VD>
Разве B и C не в одном скоупе и типы разные? Опасная практика по-моему
Здравствуйте, nikov, Вы писали:
N>Здравствуйте, VladD2, Вы писали:
TB>>>Только какой тип у этого x внутри блока? B или C?
VD>>Наиболее частный общий.
N>Он может быть неоднозначным, например, у них могут быть несколько общих интерфейсов, не связанных друг с другом. Или, например, у контравариантных делегатов Action<Array> и Action<IList<object>> есть общий базовый тип MulticastDelegate, но он не самый специфичный. Потому что у них ещё есть более узкие общие базовые типы Action<string[]>, Action<string[][]> (и много других), но ни один из них не является более специфичным чем другой, хотя они все более специфичны, чем MulticastDelegate.
Подобная ситуация например при выборе перегрузок. Как то же компилятор решает эту проблему, вот и в подобном паттерне мог бы туже логику использовать.
Здравствуйте, nikov, Вы писали:
N>Он может быть неоднозначным, например, у них могут быть несколько общих интерфейсов, не связанных друг с другом. Или, например, у контравариантных делегатов Action<Array> и Action<IList<object>> есть общий базовый тип MulticastDelegate, но он не самый специфичный. Потому что у них ещё есть более узкие общие базовые типы Action<string[]>, Action<string[][]> (и много других), но ни один из них не является более специфичным чем другой, хотя они все более специфичны, чем MulticastDelegate.
По умолчанию для переменных должен выводиться наиближайший общий тип по основному дереву наследования (т.е. без учета интерфейсов). Если сопоставляются интерфейсы, то по их иерархиям выбор осуществлять. Ну а если уж неоднозначность в интерфейсах, то сообщать об ошибке. На классах неоднозначности быть не может.
N>А по поводу твоего исходного вопроса — это можно сделать так: N>
N>if (foo() is (B or C) and var x)
N>{
N> // здесь x имеет тип A, но попадаем сюда, только если значение x имеет тип B или С (или более производный)
N>}
N>
Да, то что нужно. Как-то после функциональных языков такой подход в привычный шаблон не влез.
Спасибо!
Остаётся только вопрос, работает ли это с вложенными паттернами содержащими переменные (когда в B и C могут быть вложенные поля/свойства которые тоже хочется сопоставить с переменными). Боюсь, что нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, vaa, Вы писали:
vaa>>Разве B и C не в одном скоупе и типы разные? Опасная практика по-моему
VD>У них общие базовые типы отличные от обжекта.
ну да, но вот еще обратил внимание, что foo может быть вычислено дважды в if
если же вычислить перед if, то и переменную не нужно будет объявлять в шаблоне.