3

I have a generic class, MyClass<T>, and I want to be able to implicitly convert from some type, e.g. bool, to a specific version of the generic type, e.g. MyClass<string>. It seems I cannot use any of the following:

  • fails because "Using the generic type 'MyClass<T>' requires '1' type argument(s)":

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

  • fails because "Unbound generic name is not valid in this context" and because "User-defined conversion must convert to or from the enclosing type":

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

  • fails because "User-defined conversion must convert to or from the enclosing type":

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

  • fails because "Cannot implicitly convert type 'MyClass<string>' to 'MyClass<T>'":

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

Is there any way this can be achieved, or will I just have to live without it (and incur explicit calls to a conversion method everywhere)?

Tom
  • 4,910
  • 5
  • 33
  • 48

1 Answers1

4

No, you can't do this. The C# specification is clear, your implicit operator must convert either to or from the type in which it's declared. It has to be an exact conversion, and since the declaring type is exactly MyClass<T>, the conversion has to be either to or from that.

See e.g. Can i use a generic implicit or explicit operator? C# and C# Implicit operator with generic.

At the risk of condoning or endorsing an XY Problem, here are a couple of hacky alternatives:

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

There are a number of variations on the themes above, but they all will involve circumventing and special casing the type in some way.

It is worth pointing out that, besides the difficulty dealing with the generic vs. specific here, the use of implicit is suspect for other reasons. The documentation states right at the top that one should use implicit only "if the conversion is guaranteed not to cause a loss of data" and implementations "should never throw exceptions". In both cases, this is "so that they can be used safely without the programmer's awareness". In other words, the very nature of implicit is that they get invoked implicitly, without the programmer necessarily even thinking about it. So they must always work, which wouldn't necessarily be the case with some of the examples above (and in one example, you have to use an explicit syntax anyway, so you might as well implement the operator as explicit anyway).

None of these options are ideal. But frankly, neither is the original scenario. It is odd for a generic type to have to deal with concrete types on a specific basis. It calls into question whether the generic type really should be generic in the first place. It is possible that you really should be doing something more like the subclassing example above, only applied further. I.e. use the generic type for whatever base behavior you need, but put all your specializations into a subclass where you know the type parameter T.

I can't offer more advice than that, given the lack of details in the question. But the basic request is shaky enough to suggest that, if only the question had included a broader problem statement and details about what led you to this actual implementation goal, a better and more applicable answer might have been provided.

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • In documentation of implicit operator it is stated that they should not throw (I think even without documentation it's clear how bad it is :) ) – Evk Apr 01 '17 at 06:30
  • @Evk: yes, and I agree with that advice. The documentation also says to use `implicit` only _"if the conversion is guaranteed not to cause a loss of data"_, which is also good advice. But the OP is explicitly (sorry) asking for help with `implicit`, and I don't see a good alternative to throwing an exception when presented with a type `T` for which there is no supported conversion. That said, I think it's worth including a mention of these issues in my answer, so thanks for the reminder. – Peter Duniho Apr 01 '17 at 06:52
  • Thanks for your answer, @Peter. I had thought of both possibilities you suggested; neither of them satisfies my needs, though, as I cannot really use them `implicit`ly. What do you mean "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"? – Tom Apr 01 '17 at 22:02
  • @Tom: using `Expression` would only address performance issues; it won't change the requirement that the operator be declared in the type the operator applies to. – Peter Duniho Apr 01 '17 at 22:21
  • Thanks, @Peter. As what I want apparently can't be done, I will accept your answer. A comment re the so-called XY problem: I strive to be very exact in my questions. What I ask is what I need. Sure, the example I gave in the question was contrived and overly simplistic, but it served well to demonstrate exactly what I was looking for: implicit conversions to a specific case of a generic type (which, as said, unfortunately seems to be impossible in C#). – Tom Apr 02 '17 at 09:14