通用类型检查

时间:2020-03-05 18:39:12  来源:igfitidea点击:

有没有一种方法可以强制/限制传递给基元的类型? (布尔,整数,字符串等)

现在,我知道我们可以通过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

编辑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)!

解决方案

回答

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

这似乎很有效。

回答

使用自定义的FxCop规则,该规则标记" MyClass <>"的不受欢迎的用法。

回答

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

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

回答

@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中。

回答

如果我们可以忍受使用工厂方法(而不是我们要求的构造函数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.MyClass <T>的身份访问MyClass <T>。
  • 使用给定的MyClass &lt;T>。创建一个静态类MyClass,使用MyClass <AnyTypeItDoesntMatter>调用MyClass <T>中的静态方法(可能每次都使用适当的类型,只是为了咯咯笑)。
  • (更简单,但肯定很奇怪)创建一个抽象类型MyClass,它继承自MyClass <AnyTypeItDoesntMatter>。 (为具体起见,假设为" MyClass <int>"。)因为我们可以通过派生类的名称调用基类中定义的静态方法,所以现在可以使用" MyClass.FromString"。

这使我们可以进行静态检查,但要花更多的篇幅。

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

回答

我们可以使用typeof(PrimitiveDataType).IsPrimitive属性来简化EnforcePrimitiveType方法。我想念什么吗?

回答

@Rob,因为Enum的TypeCode是Integer,它会通过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