So what is different between <T> and <in T>?
Разница заключается в том, что in T
позволяет передавать более общий (менее производным) типа, чем было указано.
And what is the purpose of contravariant here?
ReSharper предлагает использовать контравариации здесь, потому что он видит, что вы передаете параметр T
в метод Validate
и хочет, чтобы вы могли расширить тип входного сигнала, что делает его менее универсальным.
В целом, контравариантность объясняется длиной в Contravariance explained и в Covariance and contravariance real world example, и, конечно, на всей документации по MSDN (есть great FAQ by the C# team).
Существует хороший пример через MSDN:
abstract class Shape
{
public virtual double Area { get { return 0; }}
}
class Circle : Shape
{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area { get { return Math.PI * r * r; }}
}
class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}
class Program
{
static void Main()
{
// You can pass ShapeAreaComparer, which implements IComparer<Shape>,
// even though the constructor for SortedSet<Circle> expects
// IComparer<Circle>, because type parameter T of IComparer<T> is
// contravariant.
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{ new Circle(7.2), new Circle(100), null, new Circle(.01) };
foreach (Circle c in circlesByArea)
{
Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
}
}
}
How can I apply the usage of contravariant in this example?
Допустим, у нас есть наши сущности:
public class Entity : IEntity
{
public string Name { get; set; }
}
public class User : Entity
{
public string Password { get; set; }
}
У нас также есть интерфейс IBusinessManager
и BusinessManager
реализации, которая принимает IBusinessValidator
:
public interface IBusinessManager<T>
{
void ManagerStuff(T entityToManage);
}
public class BusinessManager<T> : IBusinessManager<T> where T : IEntity
{
private readonly IBusinessValidator<T> validator;
public BusinessManager(IBusinessValidator<T> validator)
{
this.validator = validator;
}
public void ManagerStuff(T entityToManage)
{
// stuff.
}
}
Теперь, позволяет сказать, что мы создали общий валидатор для любого IEntity
:
public class BusinessValidator<T> : IBusinessValidator<T> where T : IEntity
{
public void Validate(T entity)
{
if (string.IsNullOrWhiteSpace(entity.Name))
throw new ArgumentNullException(entity.Name);
}
}
А теперь мы хотим передать BusinessManager<User>
и IBusinessValidator<T>
. Потому что это contravariant, я могу передать его BusinessValidator<Entity>
.
Если убрать in
ключевое слово, мы получим следующее сообщение об ошибке:
Если мы включаем его, это нормально компилируется.
Вы уже прочитали документацию? https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx –
Да, я уже прочитал. Однако у меня есть небольшая проблема с пониманием статьи. Во всяком случае, ссылка помогла. –