通用类型检查

发布于 2024-07-04 04:06:55 字数 2622 浏览 12 评论 0 原文

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

现在,我知道你可以限制泛型类型通过 where 子句传递给类型或接口实现的参数。 然而,这不符合原语的要求(据我所知),因为它们并不都有共同点(除了有人说之前的对象!: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 对此!

Is there a way to enforce/limit the types that are passed to primitives? (bool, int, string, etc.)

Now, I know you can limit the generic type parameter to a type or interface implementation via the where clause. However, this doesn't fit the bill for primitives (AFAIK) because they do not all have a common ground (apart from object before someone says! :P).

So, my current thoughts are to just grit my teeth and do a big switch statement and throw an ArgumentException on failure.


EDIT 1:

Just to clarify:

The code definition should be like this:

public class MyClass<GenericType> ....

And instantiation:

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

EDIT 2

@Jon Limjap - Good point, and something I was already considering. I'm sure there is a generic method that can be used to determine if the type is of a value or reference type.

This could be useful in instantly removing a lot of the objects I don't want to deal with (but then you need to worry about the structs that are used such as Size ). Interesting problem no? :)

Here it is:

where T: struct

Taken from MSDN.


I'm curious. Could this be done in .NET 3.x using extension methods? Create an interface, and implement the interface in the extension methods (which would probably be cleaner than a bit fat switch). Plus if you then need to later extend to any lightweight custom types, they can also implement the same interface, with no changes required to the base code.

What do you guys think?

The sad news is I am working in Framework 2!! :D


EDIT 3

This was so simple following on from Jon Limjaps Pointer.. So simple I almost want to cry, but it's great because the code works like a charm!

So here is what I did (you'll laugh!):

Code added to the generic class

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;
    }
}

Then a little utility method to check the type and throw an exception,

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.");
}

All that then needs to be done is to call EnforcePrimitiveType() in the classes constructors. Job done! :-)

The only downside, it only throws an exception at runtime (obviously) rather than design time. But that's no big deal and could be picked up with utilities like FxCop (which we don't use at work).

Special thanks to Jon Limjap on this one!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

温暖的光 2024-07-11 04:06:55
public class Class1<GenericType> where GenericType : struct
{
}

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

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

This one seemed to do the job..

永言不败 2024-07-11 04:06:55

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

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

Primitives appear to be specified in the TypeCode enumeration:

Perhaps there is a way to find out if an object contains the TypeCode enum without having to cast it to an specific object or call GetType() or typeof()?

Update It was right under my nose. The code sample there shows this:

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;
        }
    }
}

It's still an ugly switch. But it's a good place to start!

往事风中埋 2024-07-11 04:06:55

几乎就像 @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 中进行。

Pretty much what @Lars already said:

//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()

All work in .NET 2, 3 and 3.5.

ま昔日黯然 2024-07-11 04:06:55

如果您可以容忍使用工厂方法(而不是您要求的构造函数 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.FromInt32,这很烦人。 如果您想维护构造函数的私有性,没有一个很好的方法来解决这个问题,但这里有一些解决方法:

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

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

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

If you can tolerate using factory methods (instead of the constructors MyClass you asked for) you could always do something like this:

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
}

A problem here is that you would need to type MyClass<AnyTypeItDoesntMatter>.FromInt32, which is annoying. There isn't a very good way around this if you want to maintain the private-ness of the constructor, but here are a couple of workarounds:

  • Create an abstract class MyClass. Make MyClass<T> inherit from MyClass and nest it within MyClass. Move the static methods to MyClass. This will all the visibility work out, at the cost of having to access MyClass<T> as MyClass.MyClass<T>.
  • Use MyClass<T> as given. Make a static class MyClass which calls the static methods in MyClass<T> using MyClass<AnyTypeItDoesntMatter> (probably using the appropriate type each time, just for giggles).
  • (Easier, but certainly weird) Make an abstract type MyClass which inherits from MyClass<AnyTypeItDoesntMatter>. (For concreteness, let's say MyClass<int>.) Because you can call static methods defined in a base class through the name of a derived class, you can now use MyClass.FromString.

This gives you static checking at the expense of more writing.

If you are happy with dynamic checking, I would use some variation on the TypeCode solution above.

夕色琉璃 2024-07-11 04:06:55

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

You can simplify the EnforcePrimitiveType method by using typeof(PrimitiveDataType).IsPrimitive property. Am I missing something?

夜灵血窟げ 2024-07-11 04:06:55

@Rob,Enum 将通过 TypeValid 函数,因为它的 TypeCodeInteger。 我已更新该函数以检查 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

@Rob, Enum's will slip through the TypeValid function as it's TypeCode is Integer. I've updated the function to also check for 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
你的往事 2024-07-11 04:06:55

面临类似的挑战,我想知道你们对 IConvertible 界面< /a>. 它允许请求者要求的内容,并且您可以使用自己的实现进行扩展。

示例:

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

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

然而,我担心的是,它是否会误导使用您的类的潜在开发人员?

干杯 - 谢谢。

Having a similar challenge, I was wondering how you guys felt about the IConvertible interface. It allows what the requester requires, and you can extend with your own implementations.

Example:

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

I am thinking about this as a solution, all though many of the suggested was part of my selection also.

My concern is - however - is it misleading for potential developers using your class?

Cheers - and thanks.

说谎友 2024-07-11 04:06:55

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

Use a custom FxCop rule that flags undesirable usage of MyClass<>.

眼趣 2024-07-11 04:06:55

在dotnet 6中,我在使用struct时遇到了这个错误:

类型“string”必须是不可为 null 的值类型才能将其用作参数“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
{
}

In dotnet 6, I encountered this error when using struct:

The type 'string' must be a non-nullable value type in order to use it as parameter 'T'

So I use IConvertible instead

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
{
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文