【问题标题】:Cast Class<T> to Class将 Class<T> 转换为 Class
【发布时间】:2019-12-16 12:09:30
【问题描述】:

我有一个简单的类,看起来像这样:

public class MyClass<T> 
{
    public int Status { get; set; }
    public T Value { get; set; }
}

为方便起见,我有另一个继承自MyClass&lt;string&gt; 的类,以允许我在没有泛型参数的情况下构造MyClass,例如:

public class MyClass : MyClass<string> { }

我希望能够将MyClass&lt;T&gt; 转换为MyClass,默认情况下这似乎不起作用。此示例转换引发以下错误:

MyClass<string> withT = new MyClass<string> { Status = 1, Value = "Somevalue" };
MyClass withoutT = (MyClass)withT;

无法将“MyClass`1[System.String]”类型的对象转换为“MyClass”

所以我认为我需要实现一些隐式/显式转换逻辑,如this answer. 中所述

我已将MyClass&lt;T&gt; 更新如下,但仍然抛出相同的错误:

public class MyClass<T>
{
    public int Status { get; set; }
    public T Value;

    public static implicit operator MyClass(MyClass<T> myClass)
    {
        return new MyClass
        {
            Status = myClass.Status,
            Value = myClass.Value.GetType() == typeof(string) ? myClass.Value.ToString() : null
        };
    }
}

谁能帮忙指出我哪里出错了?

【问题讨论】:

  • 您已经创建了MyClass&lt;string&gt; 的实例。这不是MyClass 的实例。这就像尝试做的那样:object x = new object(); string y = (string) x;.
  • 如何定义非泛型MyClass?
  • 每个MyClass 都是MyClass&lt;string&gt;,但不是每个MyClass&lt;string&gt; 都是MyClass。
  • 就转换失败的原因而言 - 通常您根本无法在类型层次结构中定义转换,但我相信泛型有效地隐藏了问题 - 因为如果您尝试从MyClass&lt;int&gt; 到 MyClass 然后它会工作。但我建议完全重新考虑您的 MyClass 非泛型类型。如果这真的只是“我想构造一个MyClass&lt;string&gt; 而不必指定类型参数”的问题,则使用别名更有可能有用。
  • 请注意,转换会给您带来相当意外的行为,因为您将创建一个新的独立实例 - 而读者可能会期望它只是一个简单的转换,保留对象身份。跨度>

标签: c# generics casting


【解决方案1】:

为什么不使用工厂?这是一个非常简单的例子:

public static class MyClass
{
    public static MyClass<string> Create(string s, int status)
    {
        return new MyClass<string>(s, status);
    }
}

...

var x = MyClass.Create("Foo", 0);

【讨论】:

    【解决方案2】:

    考虑将MyClass&lt;T&gt; 设为必须继承的类(使用abstract 关键字),这样它就不能被实例化。这样你只能从派生类创建对象,你可以向下转换为基类。

    此外,如果您想要对派生类型的类型引用(用于强制转换),请将其包含在类型参数中。添加一个TDerived,它继承自MyClass&lt;T&gt;:

    public abstract class MyClass<TDerived, TValue> where TDerived : MyClass<TDerived, TValue>
    {
        protected MyClass(int status, TValue value)
        {
            this.Status = status;
            this.Value = value;
        }
        public int Status { get; set; }
        public TValue Value { get; set; }
    
        public static implicit operator TDerived(MyClass<TDerived, TValue> myClass)
            => myClass as TDerived;
    }
    
    public class SpecificClass : MyClass<SpecificClass, string>
    {
        public SpecificClass(int status, string value) : base(status, value)
        {
            // The ONLY way to instantiate a MyClass<> is from a derived class
            // such as this.
        }        
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            MyClass<SpecificClass, string> my_class = new SpecificClass(-1, "ABC");
    
            SpecificClass my_specific = my_class;
        }
    }
    

    【讨论】:

      【解决方案3】:

      Jon Skeet 就在他的 cmets 中。

      完全抛弃泛型。你能做到吗?

      class X {}
      class Y : X {}
      ...
      var x = new X();
      var y = (Y)x;
      

      没有。你不能。 X 不是 Y。同样,MyClass&lt;string&gt; 不是MyClass。

      更进一步,您的强制转换代码仍然无法像您所说的那样工作,并且尝试以其他方式定义隐式或显式强制转换运算符(例如,专门使用 string 键入)甚至无法编译。

      解决方案

      • 执行 l33t 的解决方案。这是最直接的。
      • 执行@ja72 的解决方案。它有效并且是最通用的。
      • 创建您自己的进行转换的静态方法或创建进行转换的扩展方法。
      • 打破MyClass 从MyClass&lt;string&gt; 的继承并定义您的运算符。是的,这意味着一些冗余代码。

      【讨论】:

      • 我知道X 不是Y,但是我认为转换的全部目的是使用X 来创建Y(反之亦然),不管他们的关系。
      • 呃,我不会说重点是做转换。相反,强制转换是一种类型安全的方法,可以使某种类型的实例适用于不同的上下文。例如,HTTP 状态码是一个整数,但使用枚举来表示它们在编程上很方便,有时您必须在两种类型之间进行转换。
      猜你喜欢
      • 1970-01-01
      • 2018-01-03
      • 2016-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-13
      相关资源
      最近更新 更多