简体   繁体   中英

Chaining implicit operators in generic c# classes

For the following generic c# class, I'd like to convert T to K:

public abstract class ValueType<T,K> : IValueType<T> where K : ValueType<T,K>,new()
{     
    public abstract T Value { get; set; }     

    public static implicit operator ValueType<T,K>(T val) 
    {         
        K k = new K();
        k.Value = val;
        return k;    
    }
}

If I were to implement a direct operator implicit operator K(T val) it would result in a compile-time error (CS0556).

I thought I could try chaining implicit operators:

public static implicit operator K(ValueType<T,K> vt){
    return (K)val;
}

but the following example still complains that it can't be converted:

public class Test : ValueType<int, Test>
{
    public override int Value{ get; set; }
}

Test t = 6; //complains it's unable to be converted
Console.WriteLine(t.Value);

I really want to avoid explicitly casting if possible.

This question extends upon another SO question I've previously raised.

The rules for implementing your own implicit conversion logic are quite strict and you should probably become very familiar with sections 6.4.4 and 10.10.3 of the specification if you're going to do particularly complicated ones like this.

Briefly, a few key rules you should know are:

  • The type that you are defining the conversion in has to appear in either the "to" or "from" portion of the user-defined conversion. You can't make a class C that defines a conversion from E to F; C has got to be in there somewhere.
  • It is never possible to replace a built-in implicit conversion with one of your own; you can't make special behaviour happen when converting from C to object, for example.
  • User-defined implicit conversions will be "chained" with up to two built-in implicit conversions, but not with any other user-defined conversions. So for example, if you have a user-defined implicit conversion from C to D, and a built-in implicit conversion from D to IFoo, then you get an implicit conversion from C to IFoo. But if D has a user-defined implicit conversion to E, then you don't get an implicit conversion from C to E for free.

Casts are not chained by the compiler, so that way of solving the issue doesn't work.

Implicit casts are quite strict in the type checking. Your very first snippet and the Test class do work if the compiler knows the type:

ValueType<int, Test> t = 6;
Console.WriteLine(t.Value);

The problem is that your ValueType<int, Test> - from the type system point of view - is not always a Test , so that the implicit conversion doesn't apply there.

Eric Lippert wrote a blog post on this kind of generic self-referencing by the way - worth a read!

As far as I know, I don't think that you can chain casts together, sorry about that.

I've been studying how to create parsers, and if this was possible, there would have to be an indefinite loop to find the connection from T to K . I'm not sure that the C# parser would try doing that, but my money is on no, unfortunately!

Here is what I came up to. Not an answer to the OP question, but as far as I look for, from C# rules it is not possible anyway. So what I did it to implement the implicit operator in concrete class that rely on the conversion algorithm defined in the abstract class.

My classes:

public interface IInjectable<T>
{
    T Value { get; set; }
}

internal abstract class Injectable<T,P> : IInjectable<T>
    where P : Injectable<T,P>, new()
{
    public abstract T Value { get; set; }

    public static implicit operator T(Injectable<T,P> injectable) => injectable.Value;
    
    //public static implicit operator Injectable<T,P>(T value) => new P { Value = value };
    public static P Convert(T value) => new P { Value = value };        
}

internal class InjectableGuid : Injectable<Guid, InjectableGuid>
{
    public override Guid Value { get; set; } = Guid.Empty;

    public override string ToString() => Value.ToString();        

    public static implicit operator InjectableGuid(Guid guid) => Convert(guid);        
}

Usage:

Guid id = new InjectableGuid();
Console.WriteLine(id.ToString());

Guid newId = Guid.NewGuid();
Console.WriteLine("Guid.ToString() : "+newId.ToString());
InjectableGuid injected = newId;
Console.WriteLine("InjectableGuid.ToString() : "+injected.ToString());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM