Learning points:
- Type parameter
- Type argument
- Type constraint
- Open generic
- Closed generic
- IComparer<Doctor>
- IComparer<T>
where Doctor is a class and T is a type
Both are related to generics, but they refer to different things.
- IComparer<T> → generic interface definition (open generic type)
- IComparer<Doctor> → constructed generic type (closed generic type / closed constructed type)
Example:
public interface IComparer<T>
{
int Compare(T x, T y);
}
Here:
- T → type parameter
- IComparer<T> → open generic interface because T is not replaced yet
When you substitute T:
- IComparer<Doctor>
Now:
- Doctor → type argument
- IComparer<Doctor> → closed generic interface (generic type with actual type supplied)
Example:
class DoctorNameComparer : IComparer<Doctor>
{
public int Compare(Doctor x, Doctor y)
{
return string.Compare(x.Name, y.Name);
}
}
| Syntax | Name |
| IComparer<T> | Generic interface / Open generic type |
| T | Type parameter |
| IComparer<Doctor> | Closed generic type / Constructed generic type |
| Doctor | Type argument |
Note that where constraints are used only with type parameters, so they apply to open generic declarations, not to closed generic types.
Example of a generic constraint:
class Repository<T> where T : class
{
}
Here:
- T → type parameter
- where T : class → constraint on T
- Repository<T> → open generic type
Then you create closed generic types:
Repository<Employee> repo1; // OK
Repository<int> repo2; // Error (int is not class)
The constraint is checked when closing the generic type.
You cannot write:
Repository<Employee> where Employee : class // Invalid
because Employee is already a concrete type argument, not a type parameter.
Another example:
void Process<T>() where T : IDisposable
{
}
Process<FileStream>(); // Valid syntax:
Process<FileStream>() where FileStream : IDisposable // Invalid syntax:
Rule
- where → only for type parameters (T, TKey, TValue, etc.)
- Never for actual types (Doctor, Employee, string, etc.)
So constraints belong to the generic definition, not to the closed generic instance.
Generic type constraints are specified using the where keyword only.
When people say generic constraints (restricting what T can be), C# syntax uses where keyword.
Examples:
class Repository<T> where T : class
{
}
class Factory<T> where T : new()
{
}
class Service<T> where T : IDisposable
{
}
class Pair<TKey, TValue>
where TKey : notnull
where TValue : class
{
}
Common constraints:| Constraint | Meaning |
| where T : class | Reference type |
| where T : struct | Value type |
| where T : unmanaged | Unmanaged type |
| where T : notnull | Non-nullable (reference + value) type |
| where T : new() | explicit public parameterless constructor |
| where T : BaseClass | Must inherit from base class |
| where T : IInterface | Must implement interface |
| where T : U | Must derive from another type parameter |
No comments:
Post a Comment