【问题标题】:Is there a way to define implicit conversion operators in C# for specific versions of generic types?有没有办法在 C# 中为特定版本的泛型类型定义隐式转换运算符?
【发布时间】:2017-08-26 08:14:02
【问题描述】:

我有一个泛型类MyClass<T>,我希望能够从某种类型隐式转换,例如bool,指向泛型类型的特定版本,例如MyClass<string>。看来我不能使用以下任何一种:

  • 失败,因为“使用泛型类型 'MyClass' 需要 '1' 类型参数”:

    public static implicit operator MyClass(bool value) { return new MyClass<string>(value.ToString()); }

  • 失败,因为“未绑定的通用名称在此上下文中无效”并且因为“用户定义的转换必须转换为封闭类型或从封闭类型转换”:

    public static implicit operator MyClass<>(bool value) { return new MyClass<string>(value.ToString()); }

  • 失败,因为“用户定义的转换必须与封闭类型相互转换”:

    public static implicit operator MyClass<string>(bool value) { return new MyClass<string>(value.ToString()); }

  • 失败,因为“无法将类型 'MyClass' 隐式转换为 'MyClass'”:

    public static implicit operator MyClass<T>(bool value) { return new MyClass<string>(value.ToString()); }

有什么方法可以实现,还是我只能在没有它的情况下生活(并且在任何地方都会显式调用转换方法)?

【问题讨论】:

    标签: c# generics implicit-conversion


    【解决方案1】:

    不,你不能这样做。 C# 规范很明确,您的 implicit 运算符 必须 转换为或从其声明的类型转换。它必须是精确 转换,并且由于声明类型正好是MyClass&lt;T&gt;,因此转换必须是到那个或来自那个。

    参见例如Can i use a generic implicit or explicit operator? C#C# Implicit operator with generic

    冒着宽恕或认可XY Problem 的风险,这里有几个骇人听闻的替代方案:

    // Break generics by checking the type explicitly. Requires ugly casting
    // and intermediate boxing, though it's possible that with some run-time
    // use of Expressions, you could cache a delegate that would handle the
    // conversion without the boxing. It'd still be ugly though.
    class Class1<T>
    {
        public Class1(T t) { }
    
        public static implicit operator Class1<T>(bool value)
        {
            if (typeof(T) == typeof(string))
            {
                return (Class1<T>)(object)(Class1OfString)value;
            }
    
            throw new InvalidOperationException("Invalid type T");
        }
    }
    
    // Subclass the generic, and declare the conversion there. Of course, then
    // to use the conversion, you have to reference this type explicitly. Ugly.
    class Class1OfString : Class1<string>
    {
        public Class1OfString(string text) : base(text) { }
    
        public static implicit operator Class1OfString(bool value)
        {
            return new Class1OfString(value.ToString());
        }
    }
    
    class A
    {
        public static void M()
        {
            // These all compile and execute fine
            Class1OfString c1 = true;
            Class1<string> c2 = (Class1OfString)true;
            Class1<string> c3 = true;
        }
    }
    

    上面的主题有许多变体,但它们都将涉及以某种方式规避和特殊封装类型。

    值得指出的是,除了这里处理泛型与特定的困难外,implicit 的使用还有其他原因值得怀疑。 The documentation 在顶部声明应该只使用implicit “如果保证转换不会导致数据丢失” 并且实现“不应该抛出异常”时间>。在这两种情况下,这是“以便在程序员不知情的情况下可以安全地使用它们”。换句话说,implicit 的本质是它们被隐式调用,程序员甚至不需要考虑它。所以它们必须总是工作,而上面的一些例子不一定是这种情况(在一个例子中,无论如何你必须使用显式语法,所以你不妨实现运算符还是explicit)。

    这些选项都不理想。但坦率地说,原来的情况也不是。泛型类型必须在特定的基础上处理具体类型是很奇怪的。它首先质疑泛型类型是否真的应该是泛型的。您可能真的应该做一些更像上面的子类化示例的事情,只是进一步应用。 IE。将泛型类型用于您需要的任何基本行为,但将所有特化放入您知道类型参数 T 的子类中。

    鉴于问题中缺乏细节,我无法提供更多建议。但基本要求不够可靠,足以表明,如果问题包含更广泛的问题陈述和详细说明导致您实现此实际实施目标的原因,可能会提供更好、更适用的答案。

    【讨论】:

    • 在隐式运算符的文档中指出它们不应该抛出(我认为即使没有文档也很清楚它有多糟糕:))
    • @Evk:是的,我同意这个建议。该文档还说仅使用implicit “如果保证转换不会导致数据丢失”,这也是一个很好的建议。但是 OP 明确(抱歉)寻求implicit 的帮助,并且当出现不支持转换的类型T 时,我没有看到抛出异常的好选择。也就是说,我认为值得在我的回答中提及这些问题,所以感谢您的提醒。
    • 感谢您的回答,@Peter。我已经想到了你建议的两种可能性。但是,它们都不能满足我的需求,因为我不能真正使用它们implicitly。你是什​​么意思“虽然有可能通过表达式的一些运行时使用,你可以缓存一个可以在没有装箱的情况下处理转换的委托”?
    • @Tom:使用Expression 只会解决性能问题;它不会改变以运算符适用的类型声明运算符的要求。
    • 谢谢,@彼得。由于我想要的显然无法完成,我会接受你的回答。对所谓的 XY 问题的评论:我努力在我的问题中非常准确。我问的就是我需要的。当然,我在问题中给出的示例是人为的并且过于简单化,但它很好地证明了我正在寻找的内容:隐式转换为泛型类型的特定情况(不幸的是,这在C#)。
    猜你喜欢
    • 2010-11-21
    • 2020-06-06
    • 2014-08-30
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    相关资源
    最近更新 更多