【问题标题】:C#: Is operator for Generic Types with inheritanceC#:是具有继承的泛型类型的运算符
【发布时间】:2012-06-25 09:18:12
【问题描述】:

我在比较泛型类型时遇到了 is 运算符的问题。

 public interface ISomeInterface<T> where T : SomeBaseClass{
 }

 public class SomeClass : SomeBaseClass{
 }

现在我们想用 is 运算符检查类型。我们有一个实现接口 ISomeInterface 的类的实例。

不幸的是,我们面临以下问题:

 // someObject is an Instance of a class implementing interface ISomeInterface<SomeClass>
 bool isSomeBaseClass = someObject is ISomeInterface<SomeBaseClass>; // false
 bool isSomeClass = someObject is ISomeInterface<SomeClass>; // true

是否可以检查变量泛型类型?

提前致谢, 托比

【问题讨论】:

  • 请稍微清理一下你的 C&P - 你的两个测试都是一样的。 someObject 的声明也很好。
  • 运算符返回false 的原因是因为ISomeInterface&lt;SomeClass&gt; 不是 ISomeInterface&lt;SomeBaseClass&gt; 的子类型。很容易想象一个例子,将第一个向上转换到第二个会导致类型安全错误。 (例如,将IList&lt;string&gt; 转换为IList&lt;object&gt;,然后向其添加一个整数。)
  • @millimoose 这正是问题所在。但是,如果我想为 IList is IList 的对象获取 true,我该如何检查它?
  • @Tobias Kek 的解决方案可能适用于此,但我不确定何时有用。如果接口在T 上不是协变的,则这两种类型之间没有任何有意义的关系。\

标签: c# generics operators


【解决方案1】:

这称为泛型协方差,在 C# 4.0 中受支持。您可以使用 out 关键字标记通用 T 参数:

public interface ISomeInterface<out T> where T : SomeBaseClass

但这有一个限制。 T参数只能作为接口中方法的返回类型出现。

Eric Lippert 有一个关于这个主题的 series of blog posts,我邀请您阅读。

【讨论】:

    【解决方案2】:

    是的,您可以使用 inout 关键字来利用协变和逆变:

    public interface ISomeInterface<in T> where T : SomeBaseClass{
    
    }
    

    或者:

    public interface ISomeInterface<out T> where T : SomeBaseClass{
    
    }
    

    但请记住,使用关键字in 可以使用T 作为参数,否则使用out 可以使用T 作为返回类型。 p>

    协方差:

    当您可以从 X&lt;S&gt; 转换为 X&lt;B&gt; 时,类型是协变的。

    逆变:

    当您可以从X&lt;B&gt; 转换为X&lt;S&gt; 时,类型是逆变的。

    - 其中 S 是子类,B 是基类。


    我在阅读 C# 4.0 书籍时学到的一个有趣的例子是关于堆栈的。

    class Stack<T>{
       int i;
       T[] array = new T[1000];
       public void Push(T element){
           array[i++] = element;
       }
    }
    
    class BaseClass{
    }
    
    class SubClass : BaseClass{
    }
    

    事实上它解释了在这种情况下可以使用逆变,当 Stack 实现这个接口时:

    interface IPushable<in T>{
        void Push(T element);
    }
    

    然后:

    IPushable<BaseClass> stackB = new Stack<BaseClass>();
    IPushable<SubClass> stackS = stackB;
    stackS.Push(new SubClass());
    

    虽然协方差在这种情况下,当Stack实现如下接口时:

    interface IPoppable<in T>{
        T Pop();
    }
    

    那么:

    IPoppable<SubClass> stackS = new Stack<SubClass>();
    IPoppable<BaseClass> stackB = stackB;
    BaseClass baseClass = stackB.Pop();
    

    这真的很有帮助,因为它允许向上转换和向下转换而没有任何问题和编译时错误。

    【讨论】:

      【解决方案3】:

      不确定我是否正确理解了您的问题,但您可能需要Check if a class is derived from a generic class 中的回答:

      public static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
      {
         while (toCheck != null && toCheck != typeof(object))
         {
            var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
            if (generic == cur)
                  return true;
            toCheck = toCheck.BaseType;
         }
         return false;
      }
      

      【讨论】:

        【解决方案4】:

        答案从你的第二个问题开始,因为它似乎显示了你想要实现的目标:

        问:但是,如果我想对 IList&lt;string&gt; 的对象为 IList&lt;object&gt; 是正确的,我该如何检查它?

        您的推理似乎是,由于字符串继承自对象,因此您需要一个条件模式来检查一般情况下的情况。

        IList<string> stringList = new List<string>();  
        IList<object> objectList = new List<object>();
        
        
        stringList is IList<string>;
        //> true
        stringList is IList<object>;
        //> false
        objectList is IList<string>;
        //> false
        objectList is IList<object>;
        //>true
        "someString" is object
        //> true
        

        因此您只需检查 Generic-Type 构造的类型:

        以及之前分别对 IsGenericTypeIsGenericConstructedType 进行的布尔检查。

        objectList.GetType().GenericTypeArguments[0] is object
        //> true
        stringList.GetType().GenericTypeArguments[0] is object
        //> true
        

        警告:检查空情况;如果支持语言等,则使用空合并和/或空条件运算符等...为了简洁和清晰,示例中没有


        问:是否可以检查变量(继承的)泛型类型?

        除了达林和fuex的回答,进一步指点:

        • 您可以使用Type 的 IEquality 实现进行严格的类型检查:

          bool condition = (someObject != null 
                            && someObject.GetType().Equals(typeof(ISomeInterface<SomeClass>)) );
          
        • 你检查明确检查接口:

          var interfaces = someType.GetType().GetInterfaces(); 
          //DotNet4.5: var interfaces = someType.GetType()
          //               .GetTypeInfo().ImplementedInterfaces;
          bool condition = (interfaces != null && interfaces.ToList()
                           .Contains(typeof(ISomeInterface<SomeClass>)) == true);
          

        您几乎可以使用 Type 和 TypeInfo 构建任何条件检查

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-17
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-28
          相关资源
          最近更新 更多