71

有没有办法强制/限制传递给原语的类型? (布尔、整数、字符串等)

现在,我知道您可以通过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 中完成吗?创建一个接口,并在扩展方法中实现该接口(这可能比有点胖的开关更干净)。另外,如果您以后需要扩展到任何轻量级自定义类型,它们也可以实现相同的接口,而无需更改基本代码。

你们有什么感想?

可悲的消息是我在框架 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!

4

8 回答 8

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

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

于 2008-08-12T15:11:58.990 回答
40

TypeCode枚举中似乎指定了原语:

也许有一种方法可以找出对象是否包含 ,TypeCode enum而无需将其强制转换为特定对象或调用GetType()or 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;
        }
    }
}

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

于 2008-08-12T15:21:44.010 回答
22

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

于 2008-08-12T15:43:37.260 回答
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,这很烦人。如果您想保持构造函数的私有性,则没有一个很好的解决方法,但这里有一些解决方法:

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

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

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

于 2008-09-16T05:52:05.993 回答
3

@Rob, Enum's 将按原样通过TypeValid函数。我已经更新了函数来检查.TypeCodeIntegerEnum

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
于 2009-03-26T17:08:12.250 回答
2

使用自定义FxCop规则来标记MyClass<>.

于 2008-08-12T15:21:05.433 回答
2

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

于 2008-10-19T22:38:42.603 回答
2

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

例子:

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

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

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

干杯-谢谢。

于 2012-07-27T14:47:55.360 回答