2013-04-22 3 views

ответ

20

Это похоже на «Curiously Recurring Template Pattern» (но это не то же самое).

Его можно использовать (между прочим), чтобы ограничить типы параметров методов в производном классе тем же типом, что и сам производный класс.

Here's an interesting blog post from Eric Lippert on this subject.

Основное применение этого метода заключается в принуждении классов, которые производятся от 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()); 
     } 
    } 
} 
+4

Мне очень жаль, но вы не путайте шаблоны C++ с C# дженериков? –

+0

Не принцип ли тот же для C# ?; важно то, что тип ограничен таким образом. –

+4

Согласен, этот ответ неправильный. В CRTP базовый класс также является общим, что здесь не так. И если вы говорите: «База » - это то же самое, что и «Entity », вы также ошибочны, потому что в C# вы не можете получить доступ к элементам производного класса в базе таким образом. –

2

Он говорит, что T должен быть или наследовать от Entity<T>, что и имеет T, что вы ограничиваете. Очевидно, T не может быть Entity<T>, потому что это абстрактно, поэтому оно должно быть чем-то, что наследует от него.

4

Он говорит, что T должен быть типа Entity<T> или производным от этого типа

Хотя это кажется парадоксальным, что это действует, а иногда может быть полезно тоже, хотя случаи встречаются редко и часто могут быть обработаны различными проще undertand пути.

Это часто refered в C жаргон ++ как Curiously recurring template pattern

В C# возможности несколько более ограничена, чем при использовании шаблона в C++ бетонных классов этой скороговорки, как правило, выглядит как этот

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 
} 

это бы, конечно, работать без ограничения, но она все еще может иметь смысл с функциональной точки зрения или показать намерены, и это может быть необходимо в других частях код

+0

Почему DW? в этом ответе нет ничего неправильного, и он отвечает на вопрос –

12

Хотя я прокомментировал, я собираюсь вставить свое весло, потому что я хочу также отметить, от чего зависит базовый тип.

Просто: T должно наследовать Entity<T>.

Это своего рода автореферентного родового часто используются таким образом, что базовый класса может содержать производный класс типа (через 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 ;-) 
Смежные вопросы