Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit cast operator fails with "assembly is not referenced" error

This is a very uncommon problem and there are definetly many workarounds, but I would like to understand what is actually going on and why it's not working.
So I have 3 assemblies in a test solution, first assembly has type ClassA:

public class ClassA
{
    public string Name { get; set; }
}

Second assembly references first assembly and has ClassB:

public class ClassB
{
    public string Name { get; set; }

    public static explicit operator ClassA(ClassB objB)
    {
        return new ClassA
        {
            Name = objB.Name
        };
    }
}

which has an explicit operator to cast to type ClassA. Let's say that we cannot use inheritance for some reason and just using casting as a convenient way of transforming one type to another.

Now, the last assembly references second assembly (and not the first one!) and has type ClassC:

public class ClassC
{
    public string Name { get; set; }

    public static explicit operator ClassB(ClassC objC)
    {
        return new ClassB
        {
            Name = objC.Name
        };
    }
}

which uses explicit cast operator for same reason as ClassB.

Now the interesting part: if I try to cast from ClassC to ClassB in my code, like this:

ClassC objC = new ClassC();
ClassB objB = (ClassB)objC;

I get the following error:

Error 1 The type 'FirstAssembly.ClassA' is defined in an assembly that is not referenced. You must add a reference to assembly 'FirstAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

I could easily create new instance of ClassB and just initialize it with values from ClassC instance (like I do inside explicit cast operator), and it would work fine. So what is wrong here?

like image 536
Ilya Luzyanin Avatar asked Apr 10 '14 11:04

Ilya Luzyanin


1 Answers

In section, 6.4.5 User-defined explicit conversions of the C# Language Specification (version 4.0) it reads:

A user-defined explicit conversion from type S to type T is processed as follows:

• Determine the types S0 and T0. If S or T are nullable types, S0 and T0 are their underlying types, otherwise S0 and T0 are equal to S and T respectively.

• Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S0 (if S0 is a class or struct), the base classes of S0 (if S0 is a class), T0 (if T0 is a class or struct), and the base classes of T0 (if T0 is a class).

It doesn't define how the compiler will "Find the set of types" but I think it searches all relevant classes looking for candidates for the next step:

• Find the set of applicable user-defined and lifted conversion operators, U. This set consists of the user-defined and lifted implicit or explicit conversion operators declared by the classes or structs in D that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T. If U is empty, the conversion is undefined and a compile-time error occurs.

This causes it to attempt to resolve the reference to ClassA.

like image 121
Andy Jones Avatar answered Sep 20 '22 07:09

Andy Jones