Что означает ограничение этого параметра?
Я просматриваю некоторый код, и я не понимаю, что означает определенное ограничение в следующем определении класса:
internal abstract class Entity<T> : Entity
where T : Entity<T>
{ ... }
Я не понимаю, что это означает в отношении типа параметра T
.
Ответы
Ответ 1
Это похоже на " Curiously Recurring Template Pattern" (но это НЕ то же самое).
Он может использоваться (среди прочего), чтобы помочь ограничить типы параметров методов в производном классе тем же типом, что и сам производный класс.
Здесь интересное сообщение в блоге от Эрика Липперта по этому вопросу.
Основное использование этого заключается в том, чтобы заставить классы, полученные из Entity<T>
, реализовать некоторый метод, который принимает параметр того же типа, что и класс вывода.
В следующем примере кода мы объявим в классе Entity<T>
метод DoSomethingWithTheSameTypeAsMe()
, который принимает параметр типа T
.
Из-за общего ограничения это приведет к тому, что любые классы, полученные из Entity<T>
, реализуют версию DoSomethingWithTheSameTypeAsMe()
, которая принимает параметр типа класса вывода.
Это ограниченное использование, и это очень запутанно читать, поэтому я согласен с Эриком Липпертом, когда он говорит, что вам следует избегать такого кода!
using System;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main()
{
var test1 = new Derived1();
var test2 = new Derived2();
test1.DoSomethingWithTheSameTypeAsMe(test1);
test2.DoSomethingWithTheSameTypeAsMe(test2);
}
}
public class Entity
{
public string Hello()
{
return "Hello, World.";
}
}
public abstract class Entity<T>: Entity where T: Entity<T>
{
public abstract void DoSomethingWithTheSameTypeAsMe(T item);
}
public sealed class Derived1: Entity<Derived1>
{
// You are forced to implement DoSomethingWithTheSameTypeAsMe() with a param type "Derived1".
// (i.e. the parameter is the same type as 'this')
public override void DoSomethingWithTheSameTypeAsMe(Derived1 item)
{
Console.WriteLine("Doing something with a Derived1 item: " + item.Hello());
}
}
public sealed class Derived2: Entity<Derived2>
{
public override void DoSomethingWithTheSameTypeAsMe(Derived2 item)
{
Console.WriteLine("Doing something with a Derived2 item: " + item.Hello());
}
}
}
Ответ 2
Хотя я прокомментировал, я собираюсь вставить свое весло, потому что я хочу также отметить, что базовый тип получает от этого.
Просто: T
должен наследовать Entity<T>
.
Это своего рода саморегуляторный общий часто используемый так, что базовый класс может содержать производный класс type (через T
) в методах и других областях. Это просто позволяет вам бросать материал или использовать базовую ссылку в производных типах. Это может быть очень полезно, хотя я редко вижу, что он используется в нашем коде.
Отмечу, что это означает, что не означает, что базовый класс может внезапно получить доступ к производным членам. Он может по-прежнему видеть только наименьший известный тип, определенный ограничениями, если они существуют. Если ограничений не существует, object
является наименьшим известным типом. Выгода от перспективы производного типа и чистоты, которую он предоставляет для кода, который вставляется в базовый класс.
В вашем случае он будет видеть члены Entity<T>
и Entity
. Это является причиной ограничений.
Стандартное использование будет примерно таким:
public class Customer : Entity<Customer>
{
}
public abstract class Entity<T>
where T : Entity<T>
{
public T Clone(T entityToClone)
{
return default(T); // Clone code here, returns derived type.
}
}
// Grants you...
Customer clonedCustomer = currentCustomer.Clone();
// Instead of...
Customer clonedCustomer = (Customer)currentCustomer.Clone();
// Ignore ethical constraints on cloning customers and definitely do not tell your sales team that you can ;-)
Ответ 3
В нем говорится, что T должен иметь тип Entity<T>
или выводится из этого типа
Хотя это кажется парадоксальным, оно действительно и иногда может быть полезным, хотя случаи встречаются редко и часто могут обрабатываться разными способами.
Он часто упоминается в С++ lingo как Любопытно повторяющийся шаблон шаблона
В С# возможности несколько более ограничены, чем при использовании шаблона в С++. Конкретные классы этого patter обычно будут выглядеть как
class MyClass<ItemType> : Entity<MyClass<ItemType>> {
//...
}
или просто
class MyClass : Entity<MyClass> {
//...
}
Один пример того, когда это может быть полезно, - это работать с атрибутами типа.
Предположим, вы создаете список виджетов во время выполнения. Список включает все типы, которые происходят от Entity<T>
, и вы заполняете информацию на основе метаданных из атрибутов. В Entity<T>
вы можете обрабатывать это раз и навсегда
void RegisterWidget(){
var attributes = typeof(T).GetAttributes();
//do what ever you need to
}
это, конечно, будет работать без ограничения, но оно может по-прежнему иметь смысл с функциональной точки зрения или показать намерение и может потребоваться в других частях кода.
Ответ 4
В нем говорится, что T
должен быть или наследоваться от Entity<T>
, что есть ограничение T
, которое вы ограничиваете. Очевидно, что T
не может быть Entity<T>
, потому что этот абстрактный, поэтому он должен быть чем-то, что наследует от него.