Ответ 1
Итак, что отличается между <T> и < в → <// >
Разница в том, что in T
позволяет вам передавать более общий (менее производный) тип, чем указано.
И в чем же цель контравариантности здесь?
ReSharper предлагает использовать контравариантность здесь, потому что видит, что вы передаете параметр T
в метод Validate
и хотите, чтобы вы расширили тип ввода, сделав его менее общим.
В целом, контравариантность объясняется длиной в Контравариантность объясняется и в примере ковариации и контравариантности в реальном мире, и, конечно, во всей документации по MSDN (есть отличные ответы на часто задаваемые вопросы командой С#).
В 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);
}
}
}
Как я могу применить использование контравариантного в этом примере?
Скажем, у нас есть сущности:
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>
. Поскольку он контравариантный, я могу передать его BusinessValidator<Entity>
.
Если мы удалим ключевое слово in
, мы получим следующую ошибку:
Если мы включим его, это компилируется отлично.