【问题标题】:Generic Type Parameter constraints in C# .NETC# .NET 中的泛型类型参数约束
【发布时间】:2012-11-12 13:24:30
【问题描述】:

考虑以下通用类:

public class Custom<T> where T : string
{
}

这会产生以下错误:

'string' 不是有效的约束。用作约束的类型必须 可以是接口、非密封类或类型参数。

还有其他方法可以限制我的泛型类可以使用哪些类型吗?

另外,我可以限制为多种类型吗?

例如

T 只能是字符串、整数或字节

【问题讨论】:

  • 对于这些情况,您真正能做的最好的事情是创建一个包装类,或者采用更通用的东西(对象)并在构造函数中进行类型检查。漂亮吗?不,它会起作用吗?是的。
  • @sybkar,你的意思是像我的回答一样?

标签: c# .net generics constraints


【解决方案1】:
public class Custom<T> where T : string

是不允许的,因为 only T 满足的是:stringstringsealed)——这使得它作为一个泛型毫无意义。

另外,我可以限制为多种类型吗?

不 - 除非你在运行时通过反射而不是在约束中这样做(静态构造函数是一种方法 - 如果使用不正确则抛出异常)

T can only be string, int or byte

可能使用IEquatable&lt;T&gt;之类的东西,但这并没有像你想要的那样限制它,所以最终:不。

可以做的事情是通过重载的工厂访问它:

public abstract class Custom
{
    public static Custom Create(int value)
    { return new CustomImpl<int>(value); }
    public static Custom Create(byte value)
    { return new CustomImpl<byte>(value); }
    public static Custom Create(string value)
    { return new CustomImpl<string>(value); }
    private class CustomImpl<T> : Custom
    {
        public CustomImpl(T val) { /*...*/ }
    }
}

【讨论】:

  • 感谢您的回答...我自己玩了一点。如果你有时间,请看看我的回答,看看你的想法。任何建设性的批评表示赞赏! :-)
  • " public class Custom where T : string ... 是不允许的,因为唯一符合条件的 T 是: string (字符串是密封的)——这使得它作为泛型毫无意义。 " - 同意,字符串本身毫无意义,但可以说我想限制为 String、StringBuilder 和 SecureString。这会更有用,但仍然是非法的,因为字符串是密封的:-(
  • @activwerx 不是泛型支持的东西。
  • 我明白了。也许如果我对这种情况有所了解,那可能会有所帮助。假设我正在设计一个名为 Element 的对象(如 HTML / XML 元素)。该元素本质上是一个键/值对。 Key 始终是 string 类型,但 value 可以是 string 或 Element 类型(因为元素可以嵌套)。所以我需要的是一种机制,它将值的泛型类型参数限制为仅字符串或元素(可能还有 IEnumerable 用于数组、列表等)。抱歉,这似乎是实现此类对象的一种非常复杂的方式!
【解决方案2】:

根据我的经验,我会说我理解您为什么想要 stringint ... 因为泛型基类的 ID 类型为 string 或 int

但可以肯定的是,这是不可能的。正如这个 msdn 描述所说: http://msdn.microsoft.com/en-us/library/d5x73970%28v=vs.80%29.aspx

我们可以有一个约束class(引用对象,如字符串)或struct(ValueType,如int) 所以混合 string 和 int 是不可能的

注意:字符串的错误是有道理的,因为字符串是密封的,所以它不必是通用的 - 字符串 ID 是我们需要的

【讨论】:

    【解决方案3】:

    在查看了这里的答案并在自己身上玩了一点之后,我提出了以下实现,它在运行时而不是编译时检查约束。

    // This example takes 3 parameters...
    public class GenericConstraint<T1, T2, T3>
    {
        public GenericConstraint(Type type)
        {
            if (!(type is T1) || !(type is T2) || !(type is T3))
            {
                throw new Exception("This is not a supported type");
            }
        }
    }
    

    现在我从我的自定义类继承这个...

    public class Custom<T> : GenericConstraint<string, int, byte>
    {
        public Custom() : base(typeof(T))
        {
        }
    }
    

    这会引发错误:

    Custom<long> item = new Custom<long>();
    

    这不是!

    Custom<byte> item2 = new Custom<byte>();
    

    正如 Marc Gravell 所说,这不是对继承或泛型的良好使用。从逻辑上考虑,通过继承 GenericConstraint,这只是将继承限制在 this 上,也没有正确使用类型层次结构。就泛型的使用而言,这其实是很没有意义的!

    因此,我有另一种解决方案,它充当辅助方法来在运行时约束类型。这将对象从继承中解放出来,因此对类型层次结构没有影响。

    public static void ConstrainParameterType(Type parameterType, GenericConstraint constraintType, params Type[] allowedTypes)
            {
                if (constraintType == GenericConstraint.ExactType)
                {
                    if (!allowedTypes.Contains<Type>(parameterType))
                    {
                        throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
                    }
                }
                else
                {
                    foreach (Type constraint in allowedTypes)
                    {
                        if (!constraint.IsAssignableFrom(parameterType))
                        {
                            throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
                        }
                    }
                }
            }
    
    public enum GenericConstraint
        {
            /// <summary>
            /// The type must be exact.
            /// </summary>
            ExactType,
    
            /// <summary>
            /// The type must be assignable.
            /// </summary>
            AssignableType
        }
    

    这现在允许对泛型对象进行多种类型约束,即使类型是密封的等等。

    "public class Custom where T : string ... 是不允许的,因为 唯一满足的 T 是:字符串(字符串是密封的)- 制作它 作为泛型相当没有意义。”

    是的,这是没有意义的,但在某些情况下,您可能希望限制一个对象以允许,例如;字符串、StringBuilder 和 SecureString。虽然这不提供编译时约束,但它确实提供了运行时约束,以及可以在约束中使用哪些类型的一些灵活性。

    【讨论】:

    • 但是,当我们的选择受到 C# 和 .NET 的设计原则的限制时,没有变通办法。
    • @nawfal,你能详细说明一下吗?
    • @activwerx 我的意思从您的问题中完全可以看出。您不能在where 中指定多个类型参数,如intbytestring。你也不能对一种类型这样做,因为它们都是密封的。所以我的意思是,对于受语言或框架限制的事情,这些有点黑客是唯一可能的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多