Ввод общих значений (С#)

Когда я пробую это с общим классом, где this.value имеет значение T:

if (this.value.GetType() == typeof(int))
{
    ((int)this.value)++;
}
else
{
    throw new InvalidOperationException
            ("T must be an int to perform this operation");
}

Я получаю ошибку времени компиляции: "Невозможно преобразовать тип" T "в" int "

Что мне делать, чтобы выполнить интегральную операцию над this.value, когда это int?

Обратите внимание, что это всего лишь пример. Код действительно создает преобразования с generics, а "int" - всего лишь пример одного типа для T.

Ответы

Ответ 1

К сожалению, очень сложно убедить компилятор в конкретных реализациях Т. Один (противный) подход заключается в том, чтобы придать объекту посередине (обратите внимание, что это будут поля типа box и unbox):

int i = (int)(object)this.value;
i++;
this.value = (T)(object)i;

Уродливый, но он работает. В .NET 3.5 у меня есть несколько лучших оберток для общей арифметики, здесь. Класс Operator является частью MiscUtil; на самом простом уровне, я подозреваю, что AddAlternative будет работать очень хорошо:

this.value = Operator.AddAlternative(this.value, 1);

Это должно вывести неявный < T, int > автоматически или вы можете добавить их самостоятельно:

this.value = Operator.AddAlternative<T,int>(this.value, 1);

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

Я думаю, есть и ChangeType, скрывающийся где-то там...

[edit] Collin K и другие делают правильное замечание об архитектурных последствиях, но, будучи прагматичными, бывают времена, когда T действительно имеет значение так много... но я бы согласился избежать этой специализации, если это действительно необходимо, Тем не менее (в соответствии с моим комментарием к сообщению Collin), способность выполнять такие вещи, как базовая арифметика (приращение, деление Int32 и т.д.) На (например) матрицу <T> [для T в decimal/float/int/double/etc] часто очень ценно.

Ответ 2

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

public class MyClass<T>
{
 ...
}

public class IntClass : MyClass<int>
{
  public void IncrementMe()
  {
    this.value++;
  }
}

Ответ 3

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

using System;

class Foo<T>
{
  public T value;

  public void Increment()
  {
   if (value is int) value = (T)(object)(((int)(object)value)+1);
  }
}

static class Program
{
    static void Main()
    {
     Foo<int> x = new Foo<int>();
     x.Increment();
     x.Increment();
      Console.WriteLine(x.value); 
    }     
}

Ответ 4

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

namespace GenericsOne
{
    using System;

class Program
{
    static void Main(string[] args)
    {
        Sample<int> one = new Sample<int>();
        one.AddFive(10);

        // yes, this will fail, it is to show why the approach is generally not a good one.
        Sample<DateTime> two = new Sample<DateTime>();
        two.AddFive(new DateTime());
    }
}

}

namespace GenericsOne
{
    using System;
public class Sample<T>
{
    public int AddFive(T number)
    {
        int junk = 0;

        try
        {
            junk = Convert.ToInt32(number);
        }
        catch (Exception)
        {
            Console.WriteLine("Nope");
        }

        return junk + 5;
    }
}
}

Ответ 5

Вот почему мне действительно нужны числовые или операционные ограничения в С# 4.

@Marc Gravell answer - лучший способ обойти это (+1), но это удручает, как это проблема для дженериков.