【问题标题】:Casting object to generic type instance [duplicate]将对象转换为泛型类型实例
【发布时间】:2020-10-22 14:40:11
【问题描述】:

我有一个类 Option<T> 定义如下:

class Option<T>
{
     T Value { get; set; }
     bool HasValue { get; }
}

在我的代码的某个时刻,这个类的一个实例通过回调作为object 发送给我(在value 内部传递)

private void Callback(object value)
{
}

我想将值转换为Option&lt;T&gt;,但在编译时我不知道T 的类型。 如何“向上转换”实例以便能够检查,例如,Value 属性?

【问题讨论】:

  • "我想将值强制转换为 Option,但编译时不知道类型 T" 那么你不能强制转换,因为强制转换是编译时概念。您需要使用反射或dynamic
  • 如果您不知道类型,您想如何检查Value 属性?你能给我们一个关于你想用那个Option&lt;T&gt; 对象做什么的sn-p,它可能不会在C# 中编译但传达你的意图?
  • 如果您可以更改类,请使用object Value 属性实现非泛型IOption 接口。
  • 如果您不知道要转换的类型,您应该如何转换...?不可能。 Option&lt;T&gt; 只是一个类型的蓝图。在运行时T 是一个实际类型。
  • 那么,如何使用 Reflection 来检查 HasValue?

标签: c# .net generics casting


【解决方案1】:

您在 cmets 中告诉我们的唯一一件事是您想访问 HasValue。那很简单——创建一个界面

public interface IOption
{
    bool HasValue { get; }
}

class Option<T> : IOption
{
     T Value { get; set; }
     bool HasValue { get; }
}

然后将您收到的object 转换为IOption。如果您还需要提取值,则需要一个带有covariant type parameter 的附加接口

public interface IOption<out T> : IOption
{
    T Value { get; }
}

class Option<T> : IOption<T>
{
    T Value { get; set; }
    bool HasValue { get; }
}

然后您将能够将您的object 转换为IOption&lt;object&gt;,并且对于任何T 都将成功,从而允许您将Value 检索为object。不过,您将无法设置它。

【讨论】:

  • 我忘了说Option是第三方类,所以不能让它实现任意接口。
【解决方案2】:

如果您知道所有可能性,则可以使用模式匹配:

        public static void Main(string[] args) 
        {
            object someOption = new Option<DateTime>() { Value = DateTime.Now};
            
            switch (someOption)
            {
                case Option<DateTime> dt: 
                    Console.WriteLine($"Date: {dt.Value}");
                    break;
                case Option<string> str:
                    Console.WriteLine($"String: {str}");
                    break;
            }
        }

否则,这可能是您的解决方案。当我需要将对象传递给其他泛型方法或在泛型类型中使用它时,我有时会使用这样的 conecpt。

        interface IOptionHandler
        {
            void Handle(object option);
        }
        static class OptionHandler
        {
            public static IOptionHandler Create(Type targetType)
            {
                // normally you would check if it really is an Option<T> 
                // by providing some non-generic interface like IOption
                // we skip this part

                var typeParam = targetType.GetGenericArguments()[0];
                var helperType = typeof(Helper<>).MakeGenericType(typeParam);
                return (IOptionHandler) Activator.CreateInstance(helperType);
            }

            class Helper<T> : IOptionHandler
            {
                public void Handle(object option)
                {
                    // in this context you can use all the generic powers again
                    Option<T> myOlOption = (Option<T>) option;

                    List<Option<T>> whatever = new List<Option<T>>();
                    whatever.Add(myOlOption);

                    Console.WriteLine($"{myOlOption.Value.GetType()}: {myOlOption.Value}");
                }
            }
        }

        public static void Main(string[] args) 
        {
            object someOption = new Option<DateTime>() { Value = DateTime.Now};

            var handler = OptionHandler.Create(someOption.GetType());
            handler.Handle(someOption);
        }

我通常缓存创建的辅助对象,但这完全取决于用例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-20
    • 1970-01-01
    相关资源
    最近更新 更多