如何在C#中将具有object类型的变量转换为具有泛型的类?

时间:2018-08-23 20:00:27

标签: c# generics casting clr

我有一个用C#编写的项目。我试图将对象转换为接受枚举对象作为通用参数的类。

我有以下课程

public class GenericEnumViewModel<TEnum> where TEnum : struct
{
    [Required]
    public TEnum? Value { get; set; }
    public IEnumerable<SelectListItem> Options { get; set; }
}

这是我的一个枚举对象的示例

public enum TestEnum
{
    First,
    Second,
    Third,
    Fourth
}

最后,我有一个变量,其类型为object

object obj = new GenericEnumViewModel<TestEnum>(); // Notice at this point I don't know that the generic type is `TestEnum` it could be any Enum.

如何检查变量obj是否实现GenericEnumViewModel<Enum>?另外,如何制作一个名为castedObj的新变量GenericEnumViewModel<Enum>

我正在尝试访问castedObj.ValuecastedObj.Options

我希望能够做这样的事情

var castedObj = obj as GenericEnumViewModel<Enum>;

if(castedObj != null)
{
      // do something with castedObj.Value and castedObj.Options.
}

3 个答案:

答案 0 :(得分:1)

  

如何检查变量obj是否实现GenericEnumViewModel?

您可以使用反射来做到这一点:

object obj = new GenericEnumViewModel<TestEnum>();

var objType = obj?.GetType();
var enumType =
    objType != null && objType.IsGenericType && objType.GetGenericTypeDefinition() == typeof(GenericEnumViewModel<>) ?
    objType.GetGenericArguments()[0] :
    throw new InvalidOperationException($"Object is not a closed type of {typeof(GenericEnumViewModel<>).FullName}");
  

我又如何创建一个名为castedObj的新变量作为GenericEnumViewModel

由于多种原因,这是不可能的。以下行将无法编译:

// this code is invalid!
GenericEnumViewModel<Enum> model = new GenericEnumViewModel<TestEnum>();

首先,covariance仅允许用于C#中的接口,数组和委托。

此外,即使允许类使用协方差,System.Enum还是引用类型,而枚举是值类型。将枚举值分配给System.Enum变量涉及boxing。这本身会使上面的分配变得不可能。

但是,您可以借助非通用接口(或抽象基类)来解决此问题:

public interface IGenericEnumViewModel
{
    Enum Value { get; }
    GenericEnumViewModel<SelectListItem> Options { get; }
}

public class GenericEnumViewModel<TEnum> : IGenericEnumViewModel where TEnum : struct
{
    [Required]
    public TEnum? Value { get; set; }
    public GenericEnumViewModel<SelectListItem> Options { get; set; }

    Enum IGenericEnumViewModel.Value => Value.HasValue ? Value.Value : (Enum)null;
}

// ...

if (obj is IGenericEnumViewModel castedObj)
{
    // do something with castedObj.Value and castedObj.Options.
    Enum value = castedObj.Value;
    // ...
}

奖金

从C#7.3 you can use Enum in generic type constraints开始!

public class GenericEnumViewModel<TEnum> : IGenericEnumViewModel where TEnum : struct, Enum
{
    // ...
}

答案 1 :(得分:0)

您不能这样做。 GenericEnumViewModel<TEnum>open constructed type,因为它没有确切指定泛型参数的类型。 CLR不允许构造任何开放类型的实例,并且您不能在通用类型代码本身之外使用开放类型声明变量。您可以在GenericEnumViewModel<TEnum>类中这样做:

 GenericEnumViewModel<TEnum> viewModel;

但是您不能在GenericEnumViewModel<TEnum>类之外进行操作。毕竟TEnum关键字永远不在GenericEnumViewModel<TEnum>之外。

因此,您不能在GenericEnumViewModel<TEnum>之外这样做

var castedObj = obj as GenericEnumViewModel<Enum>;

因为与

是同一回事
GenericEnumViewModel<TEnum> viewModel = obj as GenericEnumViewModel<Enum>;

使用var时,编译器应推断变量类型并创建具有推断类型的变量。但是它不能创建GenericEnumViewModel<TEnum>类型的变量,因为它是开放类型,并且因为TEnum在这种情况下不存在。

答案 2 :(得分:-1)

您可以添加新接口并明确实现它。然后,您可以使用C#7.0中引入的模式匹配。

public interface IGenericEnumViewModel
{
    object Value { get; set; }
    GenericEnumViewModel<SelectListItem> Options { get; set; }
}

public class GenericEnumViewModel<TEnum> : IGenericEnumViewModel where TEnum : struct
{
    public TEnum? Value { get; set; }
    public GenericEnumViewModel<SelectListItem> Options { get; set; }

        object IGenericEnumViewModel.Value {
               get {return Value;}
               set {Value = (TEnum?)value;}
}

然后:

if(obj is IGenericEnumViewModel c)
{
      //c is IGenericEnumViewModel 
}