Как ограничить T типами значений с помощью ограничения?

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

public static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : int, decimal
{
    // Do stuff here
}

Любая помощь приветствуется...


person Prisoner ZERO    schedule 05.11.2010    source источник
comment
Если это единственные два типа, о которых вы заботитесь, я думаю, вы можете просто перегрузить метод. Конечно, это 4 перегрузки, но это не так уж плохо.   -  person John Bledsoe    schedule 05.11.2010


Ответы (6)


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

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

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

Таким образом, вы не можете этого сделать, но у вас есть несколько альтернатив:

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

    Поскольку у вас есть только два таких типа, я бы порекомендовал сделать это.

Вот перегрузки, которые вы бы объявили:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
    // Do stuff here
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
    // Do stuff here
}

Теперь, кроме того, если ваша обработка этих значений на самом деле не зависит от числового качества этих типов, вы просто хотите ограничить, какие типы вы можете обрабатывать, тогда вы всегда можете объявить исходный метод как ну, приватно, и вызовите этот метод из ваших перегрузок. Это по-прежнему ограничит ваш код только разрешением int или decimal, публично, но ваша реализация все равно будет общей. Не зная точно, что влечет за собой «Делайте вещи здесь», невозможно сказать, является ли это жизнеспособным вариантом или нет, но в любом случае вот код:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
    return PopulateInto<T, int>(yAxis, xAxis);
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
    return PopulateInto<T, decimal>(yAxis, xAxis);
}

private static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : struct
{
    // Do stuff here
}
person Lasse V. Karlsen    schedule 05.11.2010
comment
И мои дженерики отключены, это то, что я получаю за то, что пишу ответы в пятницу вечером, позвольте мне это исправить. - person Lasse V. Karlsen; 05.11.2010
comment
Спасибо за конструктивную критику. К сожалению, вопрос в заголовке вводит в заблуждение, потому что если мы ограничимся этим, то ответ прост: where N : struct. Однако реальный вопрос имеет только один реальный, окончательный ответ, и это невозможно с текущим разрешенным синтаксисом C#. Тем не менее, я расширил свой ответ, чтобы охватить все варианты, надеюсь, что в будущем это потратит меньше времени людей. - person Lasse V. Karlsen; 11.05.2018

Невозможно ограничить общий параметр определенным типом значения.

Однако вы можете сделать его типом значения a или struct, добавив where N : struct, но это все.

person Pieter van Ginkel    schedule 05.11.2010
comment
Альтернативный способ справиться с этим — иметь специальные перегрузки для списков int или decimal. - person Jeff Mercado; 05.11.2010

Нет никакого способа сделать это с ограничением. Однако другой подход, предполагающий, что PopulateInto может работать с универсальным N, состоит в том, чтобы сделать базовый алгоритм универсальным и частным и предложить 2 общедоступные перегрузки, которые принимают int и decimal соответственно. Это создаст аналогичный эффект

public static Chart PopulateInto<T>(
  List<T> yAxis, 
  List<decimal> xAxis) {

  return PopulateIntoCore(yAxis, xAxis);
}

public static Chart PopulateInto<T>(
  List<T> yAxis, 
  List<int> xAxis) {

  return PopulateIntoCore(yAxis, xAxis);
}

private static Chart PopulateIntoCore<T, N>(
  List<T> yAxis, 
  List<N> xAxis) where N : struct {
  ...
}
person JaredPar    schedule 05.11.2010
comment
@ Марк, да, это будет суть моего ответа;) Исправлено - person JaredPar; 05.11.2010

Как сказал Питер, для этого нельзя использовать проверку времени компиляции. Однако вы можете сделать следующее во время выполнения:

if(!(typeof(N).equals(typeof(int32))) && !(typeof(N).equals(typeof(decimal))))
  // do something
person Mark Avenius    schedule 05.11.2010
comment
Да почему бы и нет. Создайте исключение InvalidOperationException. - person Hans Passant; 05.11.2010

Чтобы ответить на вопрос в названии, но не в тексте вопроса.

Чтобы охватить все типы, обычно подразумеваемые под Value Types (включая Типы значений, допускающие значение NULL, а также string, хотя это технически ссылочный тип), вам нужны 3 перегрузки:

public void Foo<T>(T arg) where T : struct
public void Foo<T?>(T? arg) where T : struct
public void Foo<string>(string arg)

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

where T : struct Аргумент типа должен быть типом значения, не допускающим значение NULL.

person Brondahl    schedule 20.11.2019

Шкаф, который вы можете получить, это Where T: struct, IComparable, IFormattable, IConvertible. Все типы значений реализуют эти интерфейсы.

person Mukesh Adhvaryu    schedule 04.05.2019