2011-01-21 1 views
1

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

Вопрос в том, в каких случаях вы решили написать собственный класс, который использует дженерики?
Практическое использование дженериков в .net (Помимо тех, что в рамке)

+0

прочитал книгу об ориентации объекта. – Jaster

+0

кажется, что вы пропустили главу о шаблонах, попробуйте завод как самый простой пример. – Jaster

ответ

2

Я использую generics каждый раз, когда мне нужно применить тот же алгоритм/ту же логику для разных типов объектов.

Пример является общим репозиторием:

public interface IRepository<T> where T : class, IEntity 
{ 
    IQueryable<T> GetQuery(); 
    void Update(T entity); 
    void Insert(T entity); 
    void Delete(int id); 
} 

public interface IEntity 
{ 
    int Id { get; set; } 
} 
2

Я использую generics всякий раз, когда класс выполняет набор операций по еще не определенному типу.

Например, представьте, что вы реализуете набор математических операций, но хотите, чтобы пользователь вашего класса смог определить фактический тип номера (int, short, ...). Не указывая тип (делая его общим), общий класс может быть более широко использован.

Другим, связанным с этим преимуществом является то, что код, сгенерированный для общего класса, будет более конкретным, используя, например. меньше операций литья и потенциально меньше тестов времени выполнения. Хорошим примером являются общие контейнеры в .NET Framework.

+0

Это хороший аргумент, не настоящий практический пример, но все же действительный –

+0

Очень практично! Я реализовал классы точек и векторов с использованием дженериков и обработки геометрических операций с ними. Время выполнения сильно зависит от типа хранилища (8 бит, 16 бит, с плавающей запятой), и какой тип использовать зависит от приложения. Используя generics, я всегда могу использовать наилучший код для конкретного приложения. –

+0

Теперь это реальный практический, спасибо –

1

Wow - слишком много примеров (которые потенциально слишком сложны для быстрого ответа SO ...), что я могу дать.

Я использую generics во многих местах (особенно для обертывания общих шаблонов, которые применяются во многих сценариях). Однако одним из наиболее полезных применений, к которым я их поставил, является ускорение отраженного или другого динамического кода, который будет меняться в зависимости от типа операнда, одновременно скрывая его.

Так, например, вымышленный, потому что мои примеры в реальном мире будут слишком вовлечены и абстрактны без большого фона), допустим, у меня есть компонент, который использует строки для извлечения значений данных из объектов почти исключительно. Конечно, если он сделал это один или два раза, то не проблема, держите отражение, но если он делает это много, то то, что вы хотите сделать, - это собрать динамические делегаты, которые могут быстрее получать значения. Так,

public static class NamedValueAccessor<T> 
{ 
    private Dictionary<string, Func<T, object>> _accessors; 
    private Dictionary<string, Action<T, object>> _writers; 

    public object GetValue(T instance, string name) { } 
    public void SetValue(T instance, string name, object newValue) { } 
} 

GetValue и SetValue может отражать более тип T для его свойств, проверяя CanRead и CanWrite значения PropertyInfo. Как только все будет подтверждено как хорошее, они могут динамически создавать делегат чтения/записи с использованием System.Linq.Expressions (легко, поскольку доступна вся информация о отражении) и кэшировать его в словаре.

О, да, забыл в моем первом черновике - обратите внимание, что делегаты SetValue могут быть скомпилированы только с выражениями в .Net 4.0, если вы не пишете IL вручную.

При этом в качестве основы вы можете использовать этот класс в других дженериков, возможно, методы расширения:

public static GetPropertyValue<T>(this T instance, string name) 
{ 
    return NamedValueAccessor<T>.GetValue(instance, name); 
} 

Which, потому что он не имеет никаких ограничений могут применяться ко всем типам.

А также отражением, если у вас есть, скажем, object - вы можете использовать метод GetType() для получения типа времени выполнения, выполните typeof(NamedValueAccessor).MakeGenericType(o.GetType()).GetMethod(...).Invoke(...). Однако по тому же шаблону в этом случае вам лучше построить динамический кеш делегатов для каждого Type, которые испечены по правильному методу NamedValueAccessor<T>.GetValue для входящего типа.

UPDATE

Таким образом, чтобы объяснить, что родовые сделали для нас в этом примере:

1)-типовые кэша собственности будет разрешены компилятором, если общий параметр типа известен во время компиляции (т. е. NamedValueAccessor<MyType>.GetValue("Foo")) компилируется в конкретный экземпляр родового. Все, что еще предстоит сделать во время выполнения, - это найти делегата в словаре и выполнить его.

2) В тайниках для отдельных типов отделяться

3) кэши могут быть загрунтованы в статическом классе статический конструктор с простым отражением над типом, чтобы получить все свойства, которые будут поддерживать и пре компиляция всех делегатов. Конечно, вы должны быть осторожны с этим кодом, чтобы убедиться, что он не может выполнить ошибку времени выполнения - никто не любит TypeInitializationException s. Если вы это сделаете, вам также не придется беспокоиться о многопоточных средах - кеш неизменен.

В точке 3 - если тип является IDynamicObject, то, конечно, эта стратегия выходит из строя, но вы можете делегировать динамический интерфейс экземпляра.

END UPDATE

На практике я использую это делать более сложные вещи, чем просто обертывание сеттеры собственности и добытчиками (код в пространстве имен ComponentModel, вероятно, лучше для этого - как в который использует Mvc), но правильно сплетенные эти решения обеспечивают отличную производительность и доступность для конечного пользователя (кодер приложения в моем случае).

Я также ожидаю, что этот ответ будет слишком абстрактным, чтобы получить какие-либо голоса, но я все равно выбрасываю его, потому что мы не даем ответов на получение голосов - но чтобы сообщить человеку, задающему вопрос :)

+0

Это тоже самое, что лежит в основе DLR, хотя кэши и логика намного глубже, чем я предлагаю здесь. –

+0

Итак, я понимаю, что вы используете эту технику для повышения производительности за счет того, что компилятор взорвался кода внутри класса generics, вместо правильного отражения? –

+0

@Cristian - собирался ответить в комментарии; но подумал, что лучше добавить дополнительную информацию в ответ, давая некоторые обоснования. –

1

Я использовал дженерики, чтобы добавить метод расширения для объектов, которые реализуют несколько интерфейсов, так

public static MyExtension<T>(this T myobject) where T : ISomeInterface, ISomeOtherInterface 
{ 
    //Do some stuff on myObject 
} 
+0

О, и автоматические дженерики в F # настолько, насколько это возможно по-человечески. – Massif