【问题标题】:Casting a generic class without specific type铸造没有特定类型的泛型类
【发布时间】:2011-01-04 14:49:43
【问题描述】:

我有以下通用类

public class Home<T> where T : Class
{
   public string GetClassType
   {
       get{ return T.ToString() }
   }
}

现在,我得到一个对象 X,我确定它是 Home:

public void DoSomething(object x)
{
    if(x is // Check if Home<>)
    {
        // I want to invoke GetClassType method of x  
        // but I don't know his generic type
        x as Home<?> // What should I use here?
    }
}

我什至可以在不指定类的泛型类型的情况下进行强制转换吗?

【问题讨论】:

    标签: c# .net generics casting


    【解决方案1】:

    如果您确定 DoSomething 的参数将是 Home&lt;T&gt;,为什么不将其设为泛型方法?

    public void DoSomething<T>(Home<T> home)
    {
       ...
    }
    

    当然,如果DoSomething 在逻辑上应该是Home&lt;T&gt; 上的实例方法,那就更容易了。

    如果你真的想坚持你所拥有的,你可以使用反射(未经测试):

    public void DoSomething(object x)
    {
        // null checks here.
    
        Type t = x.GetType();
    
        if (t.IsGenericType &&
              && t.GetGenericTypeDefinition() == typeof(Home<>))
        {
            string result = (string) t.GetProperty("GetClassType")
                                      .GetValue(x, null);
    
            Console.WriteLine(result);
        }
    
        else 
        {
            ... // do nothing / throw etc.
        }
    }
    

    【讨论】:

      【解决方案2】:

      如果 Home 派生自基类会怎样?

      public class Home
      {
          public virtual string GetClassType { get; }
      }
      public class Home<T> : Home
          where T : class
      {
          public override string GetClassType
          {
              get{ return T.ToString() } 
          }
          ...
      }
      

      然后

      public void DoSomething(object x)
      {
          if(x is Home)
          {
              string ct = ((Home)x).GetClassType;
              ...
          }
      }
      

      【讨论】:

      • 这太完美了!我有一个具有 2 个属性的通用类:SomeClass QueryList&lt;T&gt; Items。我不关心Items,只需要Query,所以将它移到基类修复它!
      【解决方案3】:

      让函数泛型怎么样?

      public void DoSomething<T>(object x)
      {
          if(x is Home<T>)
          {
              x as Home<T> ...
          }
      }
      

      编辑: 另一种可能性是创建一个包含属性GetClassName 的接口,因此您只需要检查它是否属于该接口。

      public interface IDescribeClass
      {
          public string GetClassName { get; set; }
      }
      

      顺便说一句:我会使用完整的限定名

      public string ClassType
      {
          get{ return typeof(T).FullName; }
      }
      

      【讨论】:

        【解决方案4】:

        您是否尝试过将方法定义更改为类似的内容?

        public void DoSomething<T>(Home<T> x)
        {
        
        }
        

        【讨论】:

          【解决方案5】:

          我知道这是一个旧线程,但到目前为止发布的所有答案都没有直接解决这个问题,而只是建议了解决方法(即“使用反射”、“使您的 DoSomething() 方法通用”或“创建非通用基类并调用这个基类的方法”)。

          我什至可以在不指定类的泛型类型的情况下进行强制转换吗?

          所以要清楚地回答您的问题: 不,这是不可能的。由于 C# 中的 covariance constraints 不能转换为泛型类。

          更详细地说:我假设您希望使用x as Home&lt;object&gt; 作为最低公分母,以便能够调用Object 类提供的toString()。将 object x 转换为 Home&lt;object&gt; 需要协变,这在类中是不可能的(只有泛型接口和委托可以是协变的)。虽然这对prevent mistakes at compile time 来说很棒,但在您想要访问泛型类上的方法时,这肯定是一种烦恼,就像您的情况一样。 @n8wrl 的回答可能是您在“选角”方面的最佳选择。

          话虽如此,您也可以使用基于接口的解决方案,在您的 T 参数上使用 out flag

          interface IHome<out T> {
            string GetClassType { get; }
          }
          
          public class Home<T> : IHome<T> where T : class
          {
            public string GetClassType
            {
                get { return typeof(T).Name; }
            }
          }
          

          那么这应该可以工作:

          public void DoSomething(object x)
          {
              if(x is // Check if Home<>)
              {
                  var y = x as IHome<object>;
                  var z = y.GetClassType;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-05
            • 1970-01-01
            • 2020-01-18
            • 1970-01-01
            • 2014-12-26
            相关资源
            最近更新 更多