Search code examples
c#implicit-conversion

Implicit conversion not assignable to interface


I have a class with an implicit conversion from string defined as:

class TestClass : ITestInterface
{
    public static implicit operator TestClass(string value)
    {
        return new TestClass(value);
    }

    public TestClass(string value)
    {
        Value = value;
    }
    public string Value { get; set; }
}

It implements a marker interface:

public interface ITestInterface { }

I have a method of another class defined as:

public void DoSomething(ITestInterface thing) { }

I'm getting an error trying to call that method:

public void Test()
{
    TestClass a = "This works fine.";
    DoSomething("Why doesn't this work?");
}

cannot convert from 'string' to 'Tests.ITestInterface'

All code is greatly simplified; my actually requirements are far more complex, but this seems to be the core of what is blocking the pattern I'd like to implement.

What is preventing this from working? (Something in the C# spec?)
Are there any changes I can make to my code to allow this type of casting to work?


Solution

  • You're omitting the third option that explains the issue:

    //1
    TestClass a = "This works fine.";
    
    //2
    ITestInterface i = "This doesn't work either!";
    
    //3
    DoSomething("Why doesn't this work?");
    

    In (1), you've declared TestClass a. This means that the compiler knows that when you use a different type (string, in this case), that it should try to convert said value to TestClass.

    In (2), you've declared ITestInterface i. This means that the compiler knows that when you use a different type (string, in this case), that it should try to convert said value to ITestInterface.

    That is the source of the problem. There is no conversion defined between string and ITestInterface.

    What you're currently thinking is:

    Well, I know that I want this to be converted to a TestClass. Why doesn't the compiler figure out what I want it to do?

    The short answer to that is that the compiler refuses to guess.

    What you want to happen would lead to impossible situations. For example, what would happen if there was a second class which also implements ITestInterface?

    class SecondTestClass: ITestInterface
    {
        public static implicit operator SecondTestClass(string url)
        {
            return new SecondTestClass(url);
        }
    
        public SecondTestClass(string url)
        {
            Value = GetValueFromTheInternet(url);
        }
        public string Value { get; set; }
    }
    

    Let's re-evaluate your code:

    //1
    TestClass a = "This works fine.";
    

    This works. There is a conversion from string to TestClass.

    //2
    SecondTestClass b = "This works fine.";
    

    This works. There is a conversion from string to SecondTestClass.

    //3
    ITestInterface i = "This still doesn't work!";
    
    //4
    DoSomething("This doesn't work for the same reason as //3");
    

    This doesn't work. The compiler does not have any known conversion from string to ITestInterface.

    The compiler is unable to figure out if you want this to be converted to a TestClass and then assigned to i, or if you want this to be converted to a SecondTestClass and then assigned to i.

    And, as I said before, the compiler refuses to guess.

    Also, just for clarity, this would work:

    TestClass a = "This works fine.";
    
    ITestInterface i1 = a;
    DoSomething(a);
    DoSomething(i1);
    
    SecondTestClass b = "This works fine.";
    
    ITestInterface i2 = b;
    DoSomething(b);
    DoSomething(i2);
    

    All of these assignations work.

    The crux of your problem is that the compiler wants you to explicitly state which type you want the string to be converted to. In your own example, you were already explicitly asking for a TestClass. Notice that this would not have worked if you had used var, as the compiler would not be able to figure it out in that case either.