关于c#:通用类型检查

关于c#:通用类型检查

Generic type checking

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

现在,我知道您可以通过where子句将泛型类型参数限制为类型或接口实现。但是,这并不适合基元(AFAIK),因为它们并不具有共同点(除了对象之前有人说过!:P)。

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


编辑1:

只是澄清一下:

代码定义应类似于:

1
public class MyClass<GenericType> ....

和实例化:

1
2
3
4
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)。有趣的问题不是吗? :)

这里是:

1
where T : struct

取自MSDN。


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

你们有什么感想?

不幸的消息是我正在Framework 2中工作!! :D


编辑3

这是Jon Limjaps Pointer撰写的非常简单的文章。非常简单,我几乎想哭了,但这很棒,因为代码就像一个魅力!

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

将代码添加到泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
    }
}

然后是一个小的实用程序方法来检查类型并引发异常,

1
2
3
4
5
6
7
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)!


1
2
3
public class Class1<GenericType> where GenericType : struct
{
}

这似乎很有效。


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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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已经说了差不多的话:

1
2
3
4
5
6
7
8
//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),则可以始终执行以下操作:

1
2
3
4
5
6
7
8
9
10
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.FromInt32,这很烦人。如果要保持构造函数的私密性,则没有很好的解决方法,但是这里有一些解决方法:

  • 创建一个抽象类MyClass。使MyClass< T >MyClass继承并嵌套在MyClass中。将静态方法移到MyClass。这将解决所有可见性,但必须以MyClass.MyClass< T >身份访问MyClass< T >
  • 按给定使用MyClass< T >。制作一个静态类MyClass,该类使用MyClass调用MyClass< T >中的静态方法(可能每次都使用适当的类型,仅用于咯咯笑)。
  • (更简单,但肯定很奇怪)创建一个从MyClass继承的抽象类型MyClass。 (为具体起见,我们假设MyClass。)因为您可以通过派生类的名称调用基类中定义的静态方法,所以现在可以使用MyClass.FromString

这使您可以进行静态检查,但要花费更多时间。

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


@Rob,Enum将滑过TypeValid函数,因为其TypeCodeInteger。我已经更新了该功能,还可以检查Enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

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

例:

1
2
3
4
5
    public class MyClass<TKey>
    where TKey : IConvertible
{
    // class intentionally abbreviated
}

我正在考虑将其作为解决方案,尽管许多建议也是我选择的一部分。

但是,我担心的是-使用您的课程的潜在开发人员是否会产生误解?

干杯-谢谢。


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


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


推荐阅读