### Соблюдайте последовательность ключевых слов
Последовательность ключевых слов после Where имеет значение: class или struct или unmanaged должны быть указаны в первую очередь, затем идут базовые классы или интерфейсы, и в конце new().
<br>
### Какие ключевые слова можно использовать в Where
##### class
Ограничивает тип, указывая, что он должен быть ссылочным типом.
##### struct
Ограничивает тип, указывая, что он должен быть значимым типом.
##### new()
Ограничивает тип, указывая, что он должен иметь открытый конструктор без параметров.
##### unmanaged
Ограничивает тип, указывая, что он должен быть неуправляемым типом.
Неуправляемые типы в C# - это типы, которые могут быть представлены непосредственно в памяти, без дополнительной обработки сборщиком мусора (GC) и CLR (Common Language Runtime). Это обычно примитивные типы, такие как int, float, char и bool, а также структуры, которые содержат только неуправляемые типы.
Ограничение unmanaged было добавлено в C# 7.3.
Пример использования:
```csharp
public void DoSomething<T>(T value) where T : unmanaged
{
//...
}
```
##### Имя базового класса
Ограничивает тип, указывая, что он должен быть производным от указанного базового класса.
##### Имя интерфейса
Ограничивает тип, указывая, что он должен реализовывать указанный интерфейс.
##### T
Используется для наложения ограничений на второй или последующий параметры типа, что указывает, что они должны быть производными от первого параметра типа.
```csharp
public class Pair<T, U> where U : T
{
// ...
}
```
Здесь U обязано быть подтипом T, т.е. оно должно быть либо тем же самым типом, либо типом, производным от T. Это ограничение дает вам уверенность, что вы можете безопасно использовать экземпляр U там, где ожидается T.
<br>
<br>
### Если в where указано через запятую несколько типов, то что это значит?
Если в where указано несколько типов через запятую, это означает, что тип, переданный в качестве параметра обобщения, должен соответствовать всем указанным ограничениям.
Вот пример использования нескольких ограничений:
```csharp
public class MyGenericClass<T> where T : class, IComparable<T>, new()
{
// ...
}
```
В этом примере T должен быть ссылочным типом (так как указано class), должен реализовывать интерфейс IComparable\<T> и должен иметь открытый конструктор без параметров (так как указано new()).
<br>
<br>
<br>
### Можно ли в where указать условие "или"?
Контекст:
```csharp
public bool IsOrderCompleted <TSubscribeC,
TObservableC>()
where TSubscribeC : SubscribeComponent, new()
where TObservableC : OrderComponent
```
<br>
Нет, нельзя. (Также погуглил, тоже не нашел такой возможности)
Альтернативой может быть - это использовать интерфейсы для определения общего поведения и затем применить ограничение на интерфейс.
Пример:
```csharp
public interface ICommonComponent { }
public class SubscribeComponent : ICommonComponent { }
public class OrderComponent : ICommonComponent { }
public class OtherComponent : ICommonComponent { }
public bool IsOrderCompleted<TCommonComponent>()
where TCommonComponent : ICommonComponent, new()
{
// Ваш код здесь
}
```
<br>
Теперь вы можете вызывать этот метод с разными типами, реализующими интерфейс ICommonComponent:
```csharp
bool result1 = IsOrderCompleted<SubscribeComponent>();
bool result2 = IsOrderCompleted<OrderComponent>();
bool result3 = IsOrderCompleted<OtherComponent>();
```
Если вы хотите ограничить метод только двумя конкретными типами (например, SubscribeComponent или OrderComponent), вы можете использовать перегрузку метода:
```csharp
public bool IsOrderCompleted<TSubscribeC>()
where TSubscribeC : SubscribeComponent, new()
{
// Ваш код здесь
}
public bool IsOrderCompleted<TObservableC>()
where TObservableC : Order
{
// Ваш код здесь
}
```
ChatGPT-4