【问题标题】:Generic type checking通用类型检查
【发布时间】:2008-08-12 15:07:40
【问题描述】:

有没有办法强制/限制传递给原语的类型? (bool、int、string 等)

现在,我知道您可以通过 where 子句将泛型类型参数限制为类型或接口实现。但是,这不符合原语 (AFAIK) 的要求,因为它们并非都有共同点(除了 object 在有人说之前!:P)。

所以,我目前的想法是咬紧牙关,做一个大的 switch 语句并在失败时抛出 ArgumentException


编辑 1:

澄清一下:

代码定义应该是这样的:

public class MyClass<GenericType> ....

和实例化:

MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)

编辑 2

@Jon Limjap - 好点,我已经在考虑了。我确信有一个泛型方法可以用来确定该类型是值类型还是引用类型。

这对于立即删除许多我不想处理的对象很有用(但是您需要担心使用的结构,例如 Size )。有趣的问题不是吗? :)

这里是:

where T: struct

取自MSDN


我很好奇。这可以使用扩展方法在 .NET 3.x 中完成吗?创建一个接口,并在扩展方法中实现该接口(这可能比有点胖的开关更干净)。另外,如果您以后需要扩展到任何轻量级自定义类型,它们也可以实现相同的接口,而无需更改基本代码。

你们觉得呢?

不幸的是我正在使用框架 2! :D


编辑 3

Jon Limjaps Pointer 开始,这太简单了。太简单了,我几乎想哭,但它很棒,因为代码就像一个魅力!

这就是我所做的(你会笑的!):

添加到泛型类的代码

bool TypeValid()
{
    // Get the TypeCode from the Primitive Type
    TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));

    // All of the TypeCode Enumeration refer Primitive Types
    // with the exception of Object and Empty (Null).
    // Since I am willing to allow Null Types (at this time)
    // all we need to check for is Object!
    switch (code)
    {
        case TypeCode.Object:
            return false;
        default:
            return true;
    }
}

然后是一个小工具方法来检查类型并抛出异常,

private void EnforcePrimitiveType()
{
    if (!TypeValid())
        throw new InvalidOperationException(
            "Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name + 
            "' - this Class is Designed to Work with Primitive Data Types Only.");
}

接下来需要做的就是在类构造函数中调用EnforcePrimitiveType()。任务完成! :-)

唯一的缺点是,它只会在运行时(显然)而不是设计时抛出异常。但这没什么大不了的,可以使用 FxCop 之类的实用程序(我们在工作中不使用)来获取。

在此特别感谢 Jon Limjap!

【问题讨论】:

  • 您也可以在静态构造函数中调用检查,因此每个用作泛型参数的类型仅调用一次。

标签: c# generics primitive type-safety


【解决方案1】:
public class Class1<GenericType> where GenericType : struct
{
}

这似乎完成了这项工作..

【讨论】:

    【解决方案2】:

    基元似乎在TypeCode 枚举中指定:

    也许有一种方法可以找出对象是否包含TypeCode enum,而无需将其强制转换为特定对象或调用GetType()typeof()

    更新就在我的眼皮底下。那里的代码示例显示了这一点:

    static void WriteObjectInfo(object testObject)
    {
        TypeCode    typeCode = Type.GetTypeCode( testObject.GetType() );
    
        switch( typeCode )
        {
            case TypeCode.Boolean:
                Console.WriteLine("Boolean: {0}", testObject);
                break;
    
            case TypeCode.Double:
                Console.WriteLine("Double: {0}", testObject);
                break;
    
            default:
                Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
                break;
            }
        }
    }
    

    它仍然是一个丑陋的开关。但这是一个很好的起点!

    【讨论】:

    • 好吧,我不知道如何,但对象和基元之间的主要争论点是大多数基元实际上是结构,而不是类。我会寻找一种以编程方式解决它们的方法:)
    • 好点,我已经在考虑了。我确信有一个通用方法可以用来确定类型是值还是引用类型。这可能很有用立即删除许多我不想处理的对象(但是您需要担心使用的结构,例如 Size )。有趣的问题不是吗? :) 编辑: 是的,这里是: where T : struct Taken from MSDN.
    • 我很好奇..我想知道这是否可以在 .NET 3.x 中使用扩展方法来完成..创建一个接口..在扩展方法中实现接口..(这可能比有点胖的开关更干净)。另外,如果您以后需要扩展到任何轻量级自定义类型,它们也可以实现相同的接口,而无需更改基本代码。你们有什么感想?可悲的消息是我正在使用框架 2! :D 编辑: 5 分钟后离开办公室,我回家后会去拿这个! :D
    • > public class Class1 where > GenericType : struct { } > > 这个似乎完成了这项工作。我看到的问题是,如果你有一个自定义结构,它将仍然被视为原始人,但事实并非如此。
    【解决方案3】:

    @Lars 已经说过的差不多了:

    //Force T to be a value (primitive) type.
    public class Class1<T> where T: struct
    
    //Force T to be a reference type.
    public class Class1<T> where T: class
    
    //Force T to be a parameterless constructor.
    public class Class1<T> where T: new()
    

    所有工作都在 .NET 2、3 和 3.5 中运行。

    【讨论】:

      【解决方案4】:

      如果您可以容忍使用工厂方法(而不是您要求的构造函数 MyClass),您总是可以这样做:

      class MyClass<T>
      {
        private readonly T _value;
      
        private MyClass(T value) { _value = value; }
      
        public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
        public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
        // etc for all the primitive types, or whatever other fixed set of types you are concerned about
      }
      

      这里的一个问题是您需要输入MyClass&lt;AnyTypeItDoesntMatter&gt;.FromInt32,这很烦人。如果您想保持构造函数的私有性,则没有很好的解决方法,但这里有一些解决方法:

      • 创建一个抽象类MyClass。使MyClass&lt;T&gt; 继承自MyClass 并将其嵌套在MyClass 中。将静态方法移至MyClass。这将解决所有可见性问题,但代价是必须以 MyClass.MyClass&lt;T&gt; 的身份访问 MyClass&lt;T&gt;
      • 使用给定的MyClass&lt;T&gt;。创建一个静态类MyClass,它使用MyClass&lt;AnyTypeItDoesntMatter&gt; 调用MyClass&lt;T&gt; 中的静态方法(可能每次都使用适当的类型,只是为了傻笑)。
      • (更简单,但肯定很奇怪)创建一个抽象类型MyClass 继承自MyClass&lt;AnyTypeItDoesntMatter&gt;。 (具体而言,假设为MyClass&lt;int&gt;。)因为您可以通过派生类的名称调用在基类中定义的静态方法,所以您现在可以使用MyClass.FromString

      这为您提供了静态检查,但代价是更多的写入。

      如果您对动态检查感到满意,我会在上面的 TypeCode 解决方案中使用一些变体。

      【讨论】:

        【解决方案5】:

        @Rob、Enum 将通过 TypeValid 函数,因为它的 TypeCodeInteger。我已经更新了函数来检查Enum

        Private Function TypeValid() As Boolean
            Dim g As Type = GetType(T)
            Dim code As TypeCode = Type.GetTypeCode(g)
        
            ' All of the TypeCode Enumeration refer Primitive Types
            ' with the exception of Object and Empty (Nothing).
            ' Note: must also catch Enum as its type is Integer.
            Select Case code
                Case TypeCode.Object
                    Return False
                Case Else
                    ' Enum's TypeCode is Integer, so check BaseType
                    If g.BaseType Is GetType(System.Enum) Then
                        Return False
                    Else
                        Return True
                    End If
            End Select
        End Function
        

        【讨论】:

          【解决方案6】:

          您可以使用typeof(PrimitiveDataType).IsPrimitive 属性来简化EnforcePrimitiveType 方法。我错过了什么吗?

          【讨论】:

          • 将字符串传递到该语句中返回 False,并且发帖人指定他需要字符串返回 True。 .NET 认为 char 是一种原语,但不是字符串。
          【解决方案7】:

          使用自定义 FxCop 规则来标记对 MyClass&lt;&gt; 的不当使用。

          【讨论】:

            【解决方案8】:

            遇到类似的挑战,我想知道你们对IConvertible interface 的感觉如何。它允许请求者的要求,并且您可以使用自己的实现进行扩展。

            例子:

                public class MyClass<TKey>
                where TKey : IConvertible
            {
                // class intentionally abbreviated
            }
            

            我认为这是一个解决方案,尽管许多建议也是我选择的一部分。

            我担心的是 - 然而 - 它是否会误导使用您的课程的潜在开发人员?

            干杯 - 谢谢。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-05-23
              • 2018-08-20
              • 1970-01-01
              • 1970-01-01
              • 2021-02-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多