5

I'm trying to create instance of class Bar but I'm receiving an error:

"Cannot implicitly convert type ConsoleApplication1.Bar to ConsoleApplication1.BaseFoo<ConsoleApplication1.baseOutput, ConsoleApplication1.baseInput>"

Any idea what I'm missing or what I'm doing wrong? Any advice will be nice.

public class baseOutput
{
    public string output;
}

public class baseInput
{
    public string input;
}

public class ExtendOutput : baseOutput
{
    public long id;
}

public class ExtendInput : baseInput
{
    public long id;
}

public class BaseFoo<baseOutput, baseInput>
{
    protected virtual void DoSmth()
    {

    }
}

public class Bar : BaseFoo<ExtendOutput, ExtendInput>
{
    protected override void DoSmth()
    {
        base.DoSmth();
    }
}

public class Test
{
    public void Show()
    {

    }

    private BaseFoo<baseOutput, baseInput> CreateInstance()
    {
        return new Bar(); // Error right here
    }
}
2
  • 5
    Just because A : B and C : D, does not mean that A<C> : B<D>. Look up covariance in C# - it will allow you to do what you're attempting, with a few restrictions. Commented May 28, 2016 at 9:08
  • Please stick to the C# coding convention; "public class BaseFoo<baseOutput, baseInput>" is misleading since you have classes with same names (baseOutput and baseInput). Commented May 28, 2016 at 9:30

2 Answers 2

3

I'll give you an example of why you're prevented from doing that.

Imagine instead, your classes were written like this:

public class BaseFoo<TOutput, TInput>
    where TOutput : BaseOutput
{
    public TOutput Something { get; set; }
}

public class Bar : BaseFoo<ExtendOutput, ExtendInput>
{

}

public class BaseInput { }
public class BaseOutput { }
public class ExtendOutput : BaseOutput { }
public class SomethingElse : BaseOutput { }

Now, you have this method:

private BaseFoo<BaseOutput, BaseInput> CreateInstance()
{
    //At this point, Something will be of type ExtendOutput.
    return new Bar();
}

So, we call it like this:

var myBar = CreateInstance();

Now, mybar.Something is of type BaseOutput. That's fine, though, because ExtendOutput : BaseOutput, right? Not quite.

What happens when we do this:

myBar.Something = new SomethingElse();

That's valid, because Something expects a BaseOutput, and SomethingElse is a BaseOutput. However, the object is actually a Bar, which explicitly says it should be an ExtendOutput.

The problem is clearer if we attempt to cast it back:

var myBaseFoo = CreateInstance();
myBaseFoo.Something = new SomethingElse();
Bar myBar = (Bar)myBaseFoo;
myBar.Something; // Here, we're told it's going to be an `ExtendOutput`, 
                 // but we get a `SomethingElse`?

That's clearly wrong. And that's why you're prevented from doing what you're trying to do. You can have this behavior with covariance.

Covariance makes it illegal to pass in a TOutput. So, this line

public TOutput Something { get; set; }

Would be invalid. We would only be allowed to expose the getter:

public TOutput Something { get; }

Which alleviates the above problem

Sign up to request clarification or add additional context in comments.

Comments

1

Bar is BaseFoo<ExtendOutput, ExtendInput>, and CreateInstance() requires BaseFoo<baseOutput, baseInput> to be returned, so it can't return Bar which is BaseFoo<ExtendOutput, ExtendInput>.

Regardless ExtendOutput inherits baseOutput, when you inherit a generic class the inheritance is invariant.

Consider using interfaces with in and out generic modifiers:

public class baseOutput
{
    public string output;
}

public class baseInput
{
    public string input;
}

public class ExtendOutput : baseOutput
{
    public long id;
}

public class ExtendInput : baseInput
{
    public long id;
}

public interface IBaseFoo<out T1, out T2>
{
    public void DoSmth();
}

public class Bar : IBaseFoo<ExtendOutput, ExtendInput>
{
    public void DoSmth()
    {

    }
}

public class Test
{
    public void Show()
    {

    }

    private IBaseFoo<baseOutput, baseInput> CreateInstance()
    {
        return new Bar();
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.