2010-05-03 2 views
6

У меня есть общая функция, которая ограничена структурой. Мои входы в коробке («объекты»). Можно ли отключить значение во время выполнения, чтобы избежать необходимости проверять каждый возможный тип и выполнять листы вручную?Общая разблокировка типов значений в штучной упаковке

См вышеприведенный пример:

public struct MyStruct 
    { 
     public int Value; 
    } 

    public void Foo<T>(T test) 
     where T : struct 
    { 
     // do stuff 
    } 

    public void TestFunc() 
    { 
     object o = new MyStruct() { Value = 100 }; // o is always a value type 

     Foo(o); 
    } 

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

спасибо.

+0

Интересный вопрос. Информация есть все, но, похоже, не существует способа «обобщенного перевода в производную». Очень расстраивает. Кто-то упомянул динамику (потребности 4.5). И мне интересно, можно ли что-то построить с помощью деревьев выражений. –

ответ

7

.NET Generics реализованы таким образом, который позволяет использовать типы значений в качестве параметра общего типа без каких-либо накладных расходов в боксе/распаковке. Поскольку вы бросаете объект перед вызовом Foo, вы не пользуетесь этим, на самом деле вы даже не пользуетесь дженериками вообще.

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

Ваш TestFunc может быть написано, как это без проблем:

public void TestFunc() 
{ 
    MyStruct o = new MyStruct() { Value = 100 }; // o is always a value type 

    Foo<MyStruct>(o); 
} 

Глядя на Foo, это будет выглядеть в вашем примере:

public void Foo<T>(T test) 
    where T : struct 
{ 
    T copy = test; // T == MyStruct 
} 

EDIT:

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

public void TestFunc() 
{ 
    object o = new DateTime(); 

    MethodInfo method = this.GetType().GetMethod("Foo"); 
    MethodInfo generic = method.MakeGenericMethod(o.GetType()); 
    generic.Invoke(this, new object[] {o}); 


} 
public void Foo<T>(T test) 
    where T : struct 
{ 
    T copy = test; // T == DateTime 
} 
+0

Но что, если я не знаю, что o будет MyStruct? Это может быть любая структура или тип значения? TestFunc - это всего лишь пример, в моем приложении данные поступают из словаря , где значение может быть любым. Тем не менее, я знаю, что это должна быть структура, поэтому я бы хотел назвать Foo без проверки конкретного типа. – slurmomatic

+0

Но зачем нужен конкретный тип? Цель использования дженериков - отвлечься от конкретного типа. Вам нужно бросить объект на конкретный тип или что? Тогда вам нужно будет заглянуть в кастинг на примере. –

+0

Мне не нужен конкретный тип, но поскольку я не могу использовать «struct», мне нужно знать конкретный тип для вызова Foo (). – slurmomatic

2

Нет; вы используете object, который (по определению) не тип структуры/значения. Почему вы намеренно боксируете ценность таким образом?

+1

Все мои значения хранятся в словаре , если бы у меня был словарь , я был бы рад использовать это :) Я не «намеренно» боксирую ценность в моем реальном приложении, это был просто примером. – slurmomatic

+2

@sluromatic: Тогда я вижу трудность. К сожалению, нет способа сделать то, что вы пытаетесь сделать, не зная фактического конкретного типа значения. –

+0

Нет, вы можете использовать небольшую магию отражения (см. Мой ответ ниже). –

0

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

Когда вы фактически «закрываете» общий тип с помощью структуры, вы устраняете необходимость проверки типа времени выполнения: т.е.

Foo<MyStruct>(MyStruct test); 

Ваша реализация Foo может смело предположить, что она имеет дело со структурой.

+0

Да, но я не знаю, что это MyStruct, я знаю только, что это структура. Это может быть MyStruct2, MyStruct3, int, char и т. Д. – slurmomatic

+0

Точно, в таких случаях вы должны сделать звонок как Foo (тест MyStruct2) Foo (тест MyStruct3) и т. Д. ... Я не совсем уверен, почему вы 'd использовать generics только для того, чтобы обойти их основную функцию (параметрирование типа времени проекта) – Pierreten

+0

Ок, пример. У меня есть общий класс Value , но также интерфейс IValue (по сравнению с List и IList). Я знаю, что все мои IValues ​​- это конкретная версия Value . Но для вызова каких-либо методов значения я должен отливать конкретный тип, например. Значение , Значение и т. Д. – slurmomatic

0

(маркированы как CW, потому что вы не можете передать экземпляр ValueType к родовым требует struct, но это может быть полезно для тех, кто сталкивался этот вопрос).


Вместо того, чтобы объявить o как object, вы можете использовать тип System.ValueType, который может быть назначен только struct значения; вы не можете сохранить object в ValueType.

Однако, я честно не уверен, что это что-то делает с точки зрения (un) бокса. Обратите внимание, что ECMA-334 11.1.1 говорит:

System.ValueType не является самоценным. Скорее, это тип класса, из которого автоматически генерируются все типы значений.

+0

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

0

Я не знаю точно, что вы пытаетесь АРХИВ, но вы можете передать делегат/лямбда, чтобы распаковывать значение, и выбрать какое-то значение в структуры вы заинтересованы в:

(Обновлено этот фрагмент кода после slurmomatics комментарий)

public void Foo<TValue>(object test, Func<object, TValue> ValueSelector) 
      where TValue : struct 
{ 
    TValue value = ValueSelector(test); 

    // do stuff with 'value' 
} 

public void TestFunc() 
{ 
    object o = new MyStruct() { Value = 100 }; 

    // Do the unboxing in the lambda. 
    // Additionally you can also select some 
    // value, if you need to, like in this example 
    Foo(o, x => ((MyStruct)x).Value); 
} 

Update:

Тогда сделайте так:

public static void Foo<TUnboxed>(object test) 
        where TUnboxed : struct 
{ 
    try 
    { 
     TUnboxed unboxed = (TUnboxed)test; 
    } 
    catch (InvalidCastException ex) 
    { 
     // handle the exception or re-throw it... 
     throw ex; 
    } 

    // do stuff with 'unboxed' 
} 

public void TestFunc() 
{ 
    // box an int 
    object o = 100; 

    // Now call foo, letting it unbox the int. 
    // Note that the generic type can not be infered 
    // but has to be explicitly given, and has to match the 
    // boxed type, or throws an `InvalidCastException` 
    Foo<int>(o); 
} 
+0

Хм, нет. У меня все еще есть та же проблема, что у меня есть объект o, а не MyStruct o. Я знаю, что это должна быть структура во время выполнения, но я не знаю конкретного типа во время компиляции. – slurmomatic

+0

@slurmomatic: ok, обновил мой ответ –

+0

Хорошо, спасибо. Однако проблема остается. Я не знаю, что есть int (или MyStruct и т. Д.), Я знаю только, что это тип значения (struct). – slurmomatic

Смежные вопросы