通用类型检查

时间:2008-08-12 15:07:41

标签: c# generics primitive type-safety

有没有办法强制/限制传递给基元的类型? (bool,int,string等)

现在,我知道您可以通过 where 子句将泛型类型参数限制为类型或接口实现。然而,这不符合基元(AFAIK)的费用,因为它们并非都有共同点(除了对象之后才有人说!:P)。

所以,我目前的想法只是咬紧牙关并做一个大的开关语句并在失败时抛出 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中完成吗?创建一个接口,并在扩展方法中实现接口(这可能比胖胖的开关更干净)。此外,如果您稍后需要扩展到任何轻量级自定义类型,它们也可以实现相同的接口,而不需要对基本代码进行任何更改。

你们有什么想法?

可悲的消息是我在Framework 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关于这个!

7 个答案:

答案 0 :(得分:73)

public class Class1<GenericType> where GenericType : struct
{
}

这个似乎做了这个工作..

答案 1 :(得分:39)

基元似乎在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;
        }
    }
}

这仍然是一个丑陋的开关。但这是一个开始的好地方!

答案 2 :(得分:21)

几乎@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中完成。

答案 3 :(得分: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<AnyTypeItDoesntMatter>.FromInt32,这很烦人。如果你想维护构造函数的私有性,有一个非常好的方法,但这里有几个解决方法:

  • 创建一个抽象类MyClass。让MyClass<T>继承自MyClass 并将其嵌套在MyClass 中。将静态方法移动到MyClass。这将使所有可见性得以解决,但必须以MyClass<T> MyClass.MyClass<T>访问MyClass<T>
  • 根据指定使用MyClass。创建一个静态类MyClass<T>,使用MyClass<AnyTypeItDoesntMatter>调用MyClass中的静态方法(每次可能使用适当的类型,只是为了咯咯笑)。
  • (更容易,但肯定很奇怪)制作一个继承自MyClass<AnyTypeItDoesntMatter> 的抽象类型MyClass<int> 。 (具体来说,让我们说MyClass.FromString。)因为您可以通过派生类的名称调用基类中定义的静态方法,所以现在可以使用{{1}}。

这会以更多的写作为代价进行静态检查。

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

答案 4 :(得分:3)

@Rob,Enum将覆盖TypeValid函数,因为它是TypeCode Integer。我已将该功能更新为检查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

答案 5 :(得分:2)

使用自定义FxCop规则来标记MyClass<>的不良用法。

答案 6 :(得分:2)

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