Использование для статических универсальных классов?

Каково ключевое использование Статического Универсального Класса в C#? Когда они должны использоваться? Какие примеры лучше всего иллюстрируют свое использование?

например.

public static class Example
{
   public static ...
}

Так как Вы не можете определить дополнительные методы в них, они, кажется, несколько ограничены в их утилите. Веб-ссылки по теме недостаточны так ясно нет большого количества людей, использующих их. Вот пара:-

http://ayende.com/Blog/archive/2005/10/05/StaticGenericClass.aspx

Статический универсальный класс как словарь


Сводка ответов дана

Ключевые вопросы, кажется, "Каково различие между статическим универсальным классом со статическими методами и неуниверсальным статическим классом со статическими универсальными участниками?"

Решение, относительно которого можно использовать, кажется, вращается вокруг "Класса, должен сохранить определенное для типа состояние внутренне?"

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

25
задан Community 23 May 2017 в 12:10
поделиться

8 ответов

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

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

Пример этого шаблона есть в BCL . Методы расширения ( DataRow ) Поле () и SetField () используют (частный) статический универсальный класс System. Data.DataRowExtensions + UnboxT . Проверьте это с помощью Reflector.

24
ответ дан 28 November 2019 в 18:21
поделиться

Создание класса статическим не делает ' t добавлять какие-либо функции - это просто удобная проверка, если вы собираетесь использовать класс без его создания. И для этого есть несколько применений ...

Вы можете использовать статические универсальные классы, чтобы обойти ограничение: C # не допускает частичную специализацию. Это означает, что вы должны либо указать все параметры типа, либо ни одного. Однако это может быть излишне многословным.

Например:

static class Converter {
    public TOut Convert<TIn, TOut>(TIn x) {...}
}

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

static class ConvertTo<TOut> {
    public TOut Convert<TIn>(TIn x) {...}
}

Таким образом, вы можете разрешить вывод типа работать с типом параметра и указывать только возвращаемый тип.

(Хотя приведенный выше случай возможен, он, конечно, не требует, чтобы общий класс был статическим).


Во-вторых, (как впервые указал Стивен ) для каждого сконструированного типа существуют отдельные статические поля, и это делает статические классы отличным местом для хранения дополнительной информации о типах или комбинациях типов. По сути, это полустатическая хеш-таблица, которая использует ключи для типов.

Полустатическая таблица поиска по типам звучит немного непонятно, но на самом деле это очень и очень полезная структура, поскольку она позволяет хранить дорогостоящие результаты отражения и генерации кода там, где они почти бесплатны для поиска (дешевле чем словарь, потому что он получает JIT-редакцию, и вы избегаете вызова .GetType () ). Если вы занимаетесь метапрограммированием, это прекрасно!

Например, я использую это в ValueUtils для хранения сгенерированных хэш-функций:

//Hash any object:
FieldwiseHasher.Hash(myCustomStructOrClass);

//implementation:
public static class FieldwiseHasher {
    public static int Hash<T>(T val) { return FieldwiseHasher<T>.Instance(val); }
}

public static class FieldwiseHasher<T> {
    public static readonly Func<T, int> Instance = CreateLambda().Compile();
    //...
}

Статические универсальные методы позволяют вывод типа сделать использование действительно простым; статические поля в общих классах позволяют хранить (мета) данные практически без накладных расходов . Меня совсем не удивит, если ORM, такие как Dapper и PetaPoco , будут использовать такие методы; но он также отлично подходит для (де) сериализаторов. Ограничением является то, что вы получаете низкие накладные расходы, потому что вы привязываетесь к типу времени компиляции ; если переданный объект на самом деле является экземпляром подкласса, вы, вероятно, привязываетесь к неправильному типу - и добавление проверок, чтобы избежать такого рода, подрывает преимущество низких накладных расходов.

16
ответ дан 28 November 2019 в 18:21
поделиться

Каковы основные применения статического универсального класса в C #? Когда их следует использовать ? Какие примеры лучше всего иллюстрируют их использование?

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

Чтобы дать конкретный пример, скажем, вы пишете статический служебный класс для обработки операций со списками. Вы можете написать класс двумя способами:

// static class with generics
public static class ListOps<T>
{
    public static List<T> Filter(List<T> list, Func<T, bool> predicate) { ... }
    public static List<U> Map<U>(List<T> list, Func<T, U> convertor) { ... }
    public static U Fold<U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}

// vanilla static class
public static class ListOps
{
    public static List<T> Filter<T>(List<T> list, Func<T, bool> predicate) { ... }
    public static List<U> Map<T, U>(List<T> list, Func<T, U> convertor) { ... }
    public static U Fold<T, U>(List<T> list, U seed, Func<T, U> aggregator) { ... }
}

Но классы эквивалентны, но какой из них легче использовать? Сравните:

// generic static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps<int>.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps<int>.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps<int>.Fold(numbers, 0, (acc, x) => acc + x*x);

// vanilla static class
List<int> numbers = Enumerable.Range(0, 100).ToList();
List<int> evens = ListOps.Filter(numbers, x => x % 2 = 0);
List<string> numberString = ListOps.Map(numbers, x => x.ToString());
int sumOfSquares = ListOps.Fold(numbers, 0, (acc, x) => acc + b * b);

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

В худшем случае C # не может определить типы, и вам придется указать их вручную. Вы бы предпочли написать ListOps .Map (...) или ListOps.Map (...) ? Я предпочитаю последнее.


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

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

2
ответ дан 28 November 2019 в 18:21
поделиться

Статические поля универсального типа относятся к фактическому типу T. Это означает, что вы можете хранить кэш определенного типа внутри. Это могло быть причиной для создания статического универсального типа. Вот (довольно бесполезный, но информативный) пример:

public static TypeFactory<T> where T : new()
{
    // There is one slot per T.
    private static readonly object instance = new T();

    public static object GetInstance() {
        return instance;
    }
}

string a = (string)TypeFactory<string>.GetInstance();
int b = (int)TypeFactory<int>.GetInstance();
10
ответ дан 28 November 2019 в 18:21
поделиться

В первом сообщении в блоге, о котором вы упоминаете, показано допустимое использование (в качестве класса статического репозитория для реализации ActiveRecord):

public static class Repository<T>
{
    public static T[] FindAll { }

    public static T GetById(int id){ }

    public static void Save(T item) { }
}

Или если вы хотите реализовать другие методы сортировки для универсальных массивов:

public static class ArraySort<T>
{
    public static T[] BubbleSort(T[] source) { }

    public static T[] QuickSort(T[] source) { }
}
3
ответ дан 28 November 2019 в 18:21
поделиться

Вы правы: от них мало толку. Хотя, возможно, есть некоторые редкие случаи, которые являются исключениями. Например, что, если бы класс был репозиторием для конкретного типа, как в примере Джастина, но сохранил статическую коллекцию в качестве кеша? В общем, если он содержит состояние, а не только методы, в этом может быть смысл.

1
ответ дан 28 November 2019 в 18:21
поделиться

Статический универсальный класс так же полезен, как и любой заданный статический класс. Разница в том, что вам не нужно использовать копирование и вставку для создания версии статического класса для каждого типа, с которым вы хотите, чтобы он работал. Вы делаете класс универсальным и можете «сгенерировать» одну версию для каждого набора параметров типа.

0
ответ дан 28 November 2019 в 18:21
поделиться

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

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

static class MyClass<T> where T : IComparable
{
  public static void DoSomething(T a, T b)
  {
  }

  public static void DoSomethingElse(T a, T b)
  {
  }
}

Обратите внимание, что мне не нужно было применять ограничение ко всем членам, а только на уровне класса.

5
ответ дан 28 November 2019 в 18:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: