通用类型检查

72
有没有一种方法可以强制/限制传递给原始类型的类型?(布尔、整数、字符串等)
现在,我知道你可以通过where子句将通用类型参数限制为类型或接口实现。然而,这不适用于原始类型(AFAIK),因为它们没有共同点(除了对象之外,有人可能会说!: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能否使用扩展方法来完成这个操作?创建一个接口,并在扩展方法中实现该接口(这可能比一个大而臃肿的开关更干净)。此外,如果您以后需要扩展任何轻量级自定义类型,则它们也可以实现相同的接口,而不需要对基本代码进行任何更改。

你们觉得怎么样?

可悲的消息是,我正在使用Framework 2! :D


第三次编辑

这是跟随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!


你也可以在静态构造函数中调用该检查,这样它每种作为泛型参数使用的类型仅被调用一次。 - Julien
9个回答

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

这个看起来能胜任这项工作。


41

原始类型似乎在TypeCode枚举中指定:

也许有一种方法可以找出对象是否包含TypeCode枚举,而不必将其强制转换为特定的对象或调用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;
        }
    }
}

它仍然是一个丑陋的开关。但它是一个不错的起点!


嗯,我不知道怎么做,但对象和基元之间的主要争议点是大多数基元实际上是结构体,而不是类。我会寻找一种以编程方式解决它们的方法 :) - Jon Limjap
很好的观点,这是我已经在考虑的.. 我相信有一种通用的方法可以用来确定类型是值类型还是引用类型.. 这可以在瞬间删除我不想处理的许多对象中非常有用(但随之你需要担心使用的结构体,如“_Size_”).. 有趣的问题,不是吗? :) 编辑: 啊,是的,在这里:where T : struct 从MSDN中提取。 - Rob Cooper
我很好奇..不知道是否可以使用扩展方法在.NET 3.x中完成..创建一个接口..在扩展方法中实现该接口..(这可能比一个大而臃肿的开关更清晰)..此外,如果您稍后需要扩展到任何轻量级自定义类型,则它们也可以实现相同的接口,而无需对基本代码进行任何更改。你们觉得呢?不幸的消息是我正在使用框架2!:D 编辑:离开办公室只有5分钟了,回家后会继续处理!:D - Rob Cooper
public class Class1<GenericType> where GenericType : struct { } 这个看起来能完成任务...我发现该实现的问题是,如果你有一个自定义的结构体,它仍然会被视为原始类型,但实际并非如此。 - Jon Limjap

23
基本上就是 @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<AnyTypeItDoesntMatter>.FromInt32,这很烦人。如果你想保持构造函数的私有性,那么没有一个非常好的方法可以解决这个问题,但是有几种解决方法:
  • 创建一个抽象类 MyClass。使 MyClass<T> 继承自 MyClass,并将其嵌套在 MyClass 中。将静态方法移到 MyClass 中。这样可以解决所有可见性问题,但代价是必须将 MyClass<T> 访问为 MyClass.MyClass<T>
  • 使用给定的 MyClass<T>。创建一个静态类 MyClass,使用 MyClass<AnyTypeItDoesntMatter> 调用 MyClass<T> 中的静态方法(可能每次都使用适当的类型,只是为了好玩)。
  • (更简单,但肯定很奇怪)创建一个抽象类型 MyClass,它继承自 MyClass<AnyTypeItDoesntMatter>(具体来说,假设是 MyClass<int>)。因为你可以通过派生类的名称调用在基类中定义的静态方法,所以现在可以使用 MyClass.FromString

这样可以通过更多的编写来获得静态检查。

如果你满意动态检查,我会使用上面提到的 TypeCode 解决方案的某些变体。


3

我想知道你对IConvertible接口的感受,因为我也遇到了类似的挑战。它可以满足请求者的需求,同时你也可以自己扩展实现。

举个例子:

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

我认为这是一个解决方案,虽然其中很多建议也在我的选择范围内。
不过,我的担忧是:这样对使用你的类的潜在开发人员是否具有误导性?
谢谢。

3

将一个字符串传递到该语句会返回False,而发帖者指定需要字符串返回True。.NET认为char是原始类型,但不是字符串。 - Nicholas

3

@Rob,由于EnumTypeCodeInteger,所以它们会在TypeValid函数中被忽略。我已经更新了该函数,以便还检查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

2

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


0
在dotnet 6中,当我使用 struct 时遇到了以下错误:

类型“string”必须是非可空值类型,才能将其用作参数“T”

因此我改用 IConvertible。
var intClass = new PrimitivesOnly<int>();
var doubleClass = new PrimitivesOnly<double>();
var boolClass = new PrimitivesOnly<bool>();
var stringClass = new PrimitivesOnly<string>();
var myAwesomeClass = new PrimitivesOnly<MyAwesomeClass>(); // illegal

// The line below encounter issue when using "string" type
// class PrimitivesOnly<T> where T : struct
class PrimitivesOnly<T> where T : IConvertible
{
    
}

class MyAwesomeClass
{
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接