Подтвердить что ты не робот

Что означает ограничение этого параметра?

Я просматриваю некоторый код, и я не понимаю, что означает определенное ограничение в следующем определении класса:

internal abstract class Entity<T> : Entity
    where T : Entity<T>
{ ... }

Я не понимаю, что это означает в отношении типа параметра T.

4b9b3361

Ответ 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>, потому что этот абстрактный, поэтому он должен быть чем-то, что наследует от него.