如何在不知道类型的情况下转换通用类型

7

我正在尝试在运行时创建一个通用对象。目前我已经成功创建了它,但是我无法弄清楚如何进行转换。我拥有枚举对象并希望生成EnumMapper,将枚举值转换为自定义字符串以映射到遗留数据库。

Type enumType = myEnum.GetType();
Type enumMapperType = typeof(EnumMapper<>)
                      .GetGenericTypeDefinition().MakeGenericType(enumType);
var mapper = Activator.CreateInstance(enumMapperType); // OK
EnumMapper<> mapper = (EnumMapper<>) Activator.CreateInstance(enumMapperType); // Error

当我在调试器中检查对象时,它被创建成我所期望的那样,但是如何转换它以便我可以使用它?
这个类:
public class EnumMapper<T> : IEnumMapper<T>

接口:
public interface IEnumMapper<T>
{
    T EnumValue(string value);

    bool HasEnumValue(string stringValue);

    bool HasStringValue(T enumValue);

    string StringValue(T enumValue);
}

Error   2   ; expected  \EnumMapperTest.cs  36
Error   4   ; expected  \EnumMapperTest.cs  36
Error   1   Invalid expression term '>' \EnumMapperTest.cs  36
Error   3   Invalid expression term '>' \EnumMapperTest.cs  36
Error   34  Only assignment, call, increment, decrement, and new object expressions can be used as a statement  \EnumMapperTest.cs  36
Error   36  The name 'mapper' does not exist in the current context \EnumMapperTest.cs  36
Error   35  Using the generic type 'EnumMapper<T>' requires 1 type arguments    \EnumMapperTest.cs  36
Error   37  Using the generic type 'EnumMapper<T>' requires 1 type arguments    \EnumMapperTest.cs  36

1
你遇到了什么具体的错误? - Jonathon Reinhart
1
我相信这个问题已经有了很多重复的解答...但是您能否提供更多关于您想要实现的实例的信息,以及您是否控制泛型类型?(例如,您是否可以创建一个非泛型基础类型?) - Jon Skeet
错误 - 只有赋值、调用、增量、减量和新对象表达式可以用作语句。 - uncletall
@Jon,你能添加链接吗? - uncletall
你想以什么方式使用这个 EnumMapper<> 后面呢?也许你可以将它转换为一些不太具体的类型,比如无类型的 IList 或 IEnumerable? - Dmytro Rudenko
显示剩余2条评论
1个回答

2
据我所知,在C#中,你需要的确切内容是不可能实现的。基本上,你需要一种方法,根据普通的非泛型参数的值,使返回变量的类型不同,即如果参数是typeof(Enum1),则结果变量为EnumMapper< Enum1>,如果参数是 typeof(Enum2),则结果变量为EnumMapper< Enum2>
你可以通过使用泛型参数来实现此操作,但是由于泛型主要涉及编译时信息,而你只有在运行时才拥有该值,因此在这种情况下无法使用泛型。
你可以做的事情(也是我所做的事情)是使用动态代码解决此问题,并尽快进入静态类型领域(dynamic真的很具传染性,有人说像微笑,有人说像病毒):
public dynamic GetMapperObject(Type enumType)
{
  Type enumMapperType = typeof(EnumMapper<>)
                           .GetGenericTypeDefinition()
                           .MakeGenericType(enumType);
  var mapper = Activator.CreateInstance(enumMapperType);
  return mapper;
}

使用以下调用代码:

var mapper = GetMapperObject(enumType);
//dynamic call, bind the resut to a statically typed variable
bool result = mapper.HasEnumValue("SomeStringValue") 

(旧答案,只是在问题上添加了另一个间接层 :))

您可以将所有内容都包装在通用方法中,类似于以下内容:

public EnumMapper<T> GetMapperObject<T>()
{
  Type enumType = typeof(T);
  Type enumMapperType = typeof(EnumMapper<>)
                           .GetGenericTypeDefinition()
                           .MakeGenericType(enumType);
  var mapper = Activator.CreateInstance(enumMapperType);
  return mapper as EnumMapper<T>;
}

并使用以下方式调用:

var mapper = GetMapperObject<EnumMapperTestEnum>();

然而,如果你的枚举只有一个值,你可以使用类型推断,例如:
//everything is the same, just different signature
public EnumMapper<T> GetMapperByExample<T>(T item)
{
  Type enumType = typeof(T);
  Type enumMapperType = typeof(EnumMapper<>)
                           .GetGenericTypeDefinition()
                           .MakeGenericType(enumType);
  var mapper = Activator.CreateInstance(enumMapperType); // OK
  return mapper as EnumMapper<T>;
}

并使用类型推断调用它

var mapper = GetMapperByExample(EnumMapperTestEnum.SomeValue); 

2
问题在于我没有 T,我得到了一个枚举,然后想为这个枚举创建一个枚举映射器... - uncletall
1
如果您用函数调用替换您的代码并提取方法,那么这将起作用。 - SWeko
好的,我想我的问题没有表达清楚。我没有类型T,只有enumType。这只是为了测试目的,我才使用typeof(T)。 - uncletall
1
好的,那很有效...现在我的问题是,当我从反射中获取我的枚举时,我有更多的间接层级。 - uncletall
接受了你的答案,它有效。最终我实际上改变了接口为非泛型类型。我认为这对我来说更易读一些... - uncletall
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接