2015-01-15 2 views
4

Вопрос прост: я использую отражение, чтобы получить значение. Тогда, если это struct, я звоню метод FooStruct, иначе FooClass:Как вызвать метод с ограничением структуры до неизвестной структуры

Type type = x.GetType(); 
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 
{ 
    var val = fieldInfo.GetValue(value); 
    object obj = type.IsValueType ? val.FooStruct() : val.FooClass(); 
    fieldInfo.SetValue(x, obj); 
} 

проблема заключается в том, что FooStruct имеет ограничение:

public static T FooStruct<T>(this T value) where T : struct 
{ 
    //... 
} 

так вопрос: не является ли это можно назвать метод с ограничением struct для объекта, который содержит экземпляр с коробкой struct без отражения?

+0

Видя, как вы используете GetType(), вы уже с помощью отражения:) – Carra

+1

У нас недостаточно информации, так как вы используете только отражение в вашем примере. Пожалуйста, объясните, в чем проблема. –

+1

Вы имеете в виду: возможно ли задание объекта, содержащего экземпляр structed boxed? Вы хотите вызвать метод в переменной 'val' правильно? – helb

ответ

2

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

Практическое предложение: я просто уронить ограничение на ваших FooStruct и FooClass методов, и дополнительно:

  • либо сделать их нетипичными и принять аргумент типа object (что val объявляется как, во всяком случае). Нет никакого преимущества в том, чтобы эти методы были общими, если они только когда-либо прошли object s;

  • или литой val от object к T перед вызовом FooStruct/FooClass.

Почему это невозможно сделать, что вы просите? Вы пытаетесь преобразовать выражение, которое статически напечатано object (а именно val) во что-то, что статически напечатано <T> where T : struct или <T> where T : class (для вызова соответствующего метода расширения на таком T). То есть вы пытаетесь динамически вводить новую переменную типа внутри цикла foreach. К сожалению, единственный способ ввести переменную типа состоит в том, чтобы объявить ее заранее, то есть как некоторый общий тип параметра T в сигнатуре метода; и тогда это не код внутри ваш метод, который позволяет выбрать, какой фактический тип он обозначает —, это , вызывающий код, определяющий T.

Отражение на основе раствора:

// determine which method ought to be called based on `val`'s run-time type. 
// (for C# 6 and later, use the `nameof` operator instead of hard-coding method names) 
Type type = val.GetType(); 
string fooName = type.IsValueType ? "FooStruct" : "FooClass"; 

// bind to the generic method and supply the type argument for it: 
// (I'm assuming that your extension methods are defined in `FooMethodsClass`.) 
MethodInfo fooOpen = typeof(FooMethodsClass).GetMethod(fooName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 
MethodInfo foo = fooOpen.MakeGenericMethod(new Type[] { type }); 

// invoke the generic (extension) method with `val` as the `this` argument: 
foo.Invoke(null, new object[] { val }); 
1

Динамическая переменная поддержка будет установлена ​​T соответствующим образом. Я регулярно использую этот трюк. Попробуйте следующее:

Type type = x.GetType(); 
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 
{ 
    dynamic val = fieldInfo.GetValue(value); 
    object obj = type.IsValueType ? Utilities.FooStruct(val) : Utilities.FooClass(val); 
    fieldInfo.SetValue(x, obj); 
} 
+0

Интересно! Два вопроса: ** 1. ** Разве декларация 'Utilities.FooStruct' выглядит иначе, чем версия OP, или она точно такая же? ** 2. ** Учитывая, что мы имеем дело с 'dynamic', существует ли какое-либо преимущество в сохранении ограничений типового типа на' FooStruct' и 'FooClass'? – stakx

+0

Это должно быть то же самое заявление. (Я сделал предположение относительно имени класса утилиты, поскольку он не был указан.) – Brannon

+0

Я только что попробовал это, и я получаю 'Тип 'dynamic' должен быть невообразимым типом значения, чтобы использовать его как параметр 'T'', т.е. вы не можете передать 'dynamic' как' struct'? (Хотя во время выполнения вы не можете передать структуру в метод ограничения класса, даже если это динамический ...) – Rawling

0

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

public static class Utilities 
{ 
    public static ValueType FooStruct(this ValueType value) 
    { 
     //put your code here 
     return default(ValueType); 
    } 

    public static object FooClass(this object value) 
    { 
     //put your code here 
     return null; 
    } 

    public static T FooStruct<T>(this T value) where T: struct 
    { 
     return (T) FooStruct(value); 
    } 

    public static T FooClass<T>(this T value) where T: class 
    { 
     return (T) FooClass(value); 
    } 
} 

public class Program 
{ 
    class TestClass 
    { 
     public TestStruct StructField; 
    } 

    struct TestStruct 
    { 
     int x; 
     int y; 
    } 

    public static void Main() 
    { 
     var x = new TestClass(); 
     Type type = x.GetType(); 
     foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 
     { 
      var val = fieldInfo.GetValue(x); 
      object obj = fieldInfo.FieldType.IsValueType ? ((ValueType)val).FooStruct() : val.FooClass(); 
      fieldInfo.SetValue(x, obj); 
     } 

     //Generic call 
     var structVar = new TestStruct(); 
     structVar.FooStruct(); 
    } 
} 
1

Видимо, вы можете вызывать методы с отражением и они работают без проблем:

using System; 
using System.Reflection; 

namespace DemoDynamicT 
{ 
    public static class Utilities 
    { 
     public static T FooStruct<T>(this T value) where T:struct 
     { 
      return default(T); 
     } 

     public static T FooClass<T>(this T value) where T : class 
     { 
      return default(T); 
     } 
    } 

    public class Program 
    { 
     class TestClass 
     { 
      public TestStruct StructField; 
     } 

     struct TestStruct 
     { 
      public int x; 
      int y; 
     } 

     public static void Main() 
     { 
      var x = new TestClass(); 
      Type type = x.GetType(); 
      foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 
      { 
       var val = fieldInfo.GetValue(x); 
       var methodInfo = typeof(Utilities).GetMethod(fieldInfo.FieldType.IsValueType ? "FooStruct" : "FooClass"); 
       var toBeCalled = methodInfo.MakeGenericMethod(fieldInfo.FieldType); 
       object obj = toBeCalled.Invoke(null, new [] {val}); 
       fieldInfo.SetValue(x, obj); 
      } 
     } 
    } 
} 
Смежные вопросы