1

...which might not actually be a variance issue, after all. When I compiled my code, Visual Studio would give me the following error:

Type of conditional expression cannot be determined because there is no implicit conversion between 'ClassA' and 'ClassB'

I read up on this error here, and it sure sounded like it's not possible to use abstract classes as contracts like interfaces, where I can use derived classes in place of their base class. To make things simpler, I wrote some test classes that mimic the relationship of my actual code. Please consider the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassImplicitConversion {
    class Program {
        static void Main(string[] args) {
            StartClass temp = new StartClass();
        }
    }

    public class StartClass
    {
        public AbstractClass _ac { get; private set; }

        public StartClass()
        {
            bool which = true;
            // this block will compile
            /*
            if( which)
                _ac = new DerivedClass1();
            else 
                _ac = new DerivedClass2();
             */
            // this block will not compile
            _ac = which ? new DerivedClass1() : new DerivedClass2();
        }
    }

    public interface SomeInterface
    {
        void SiFoo();
    }

    public abstract class InnerAbstractClass
    {
        public abstract void IacFoo();
    }

    public abstract class AbstractClass : InnerAbstractClass, SomeInterface
    {
        public override void IacFoo() {

        }

        public void SiFoo() {

        }

        public abstract void OverrideMe();
        public abstract void OverrideMeToo<T>();
    }

    public class DerivedClass1 : AbstractClass
    {
        public override void OverrideMe() {}
        public override void OverrideMeToo<T>() {}
    }

    public class DerivedClass2 : AbstractClass
    {
        private int _blah;
        public override void OverrideMe() {}
        public override void OverrideMeToo<T>() {}
    }
}

The problem is with the line:

_ac = which ? new DerivedClass1() : new DerivedClass2();

If I use an inline if, I'll get that dreaded compiler error. However, if I instead use this:

if( which)
    _ac = new DerivedClass1();
else 
    _ac = new DerivedClass2();

my code compiles fine.

Can anyone explain what causes this? I've always considered the two to be equivalent. Thanks!

Dave
  • 14,618
  • 13
  • 91
  • 145
  • 1
    You could work around this by casting to the base class. – crashmstr Sep 04 '15 at 18:53
  • While it is good looking question I don't see how it is different from once you possibly looked at already. Version "why there is no implicit conversion between two sibling derived classes" could be good non-duplicate alternative, but I suspect you know answer to that already. There are other answers like http://stackoverflow.com/a/202296/477420 which may be nicer, but all essentially the same as good answer by Daniel A. White here. – Alexei Levenkov Sep 04 '15 at 20:07
  • Side note: consider updating your post with title talking about "ternary/conditional operator" - so it can be good signpost for future searches. – Alexei Levenkov Sep 04 '15 at 20:09

2 Answers2

7

It is just the way the language standard is defined:

Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.

There's no implicit conversion between DerivedClass1 and DerivedClass2 - its only through the base class that exists. The spec of the operator doesn't say it looks into the resulting value type on the assignment, so the compiler is doing what it is designed to do.

Source: https://msdn.microsoft.com/en-us/library/ty67wk28.aspx

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
1

I think you need to change the code as follows to get the effect you're looking for:

_ac = which ? (new DerivedClass1() as AbstractClass) : (new DerivedClass2() as AbstractClass);

Hope this helps!

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73