Ввод общих значений (С#)
Когда я пробую это с общим классом, где 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), но это удручает, как это проблема для дженериков.