MasterMastic
MasterMastic

Reputation: 21306

Can't cast a derived class to its base type

I have a few dialogs in my class and I'm trying to initialize them with a function:

    private void InitializeFileDialog(ref FileDialog fileDialog)
    {
        fileDialog.Filter = "Word Documents|*.doc|Excel Worksheets|*.xls|PowerPoint Presentations|*.ppt" +
         "|Office Files|*.doc;*.xls;*.ppt" +
         "|All Files|*.*";
        fileDialog.DefaultExt = "txt";
    }

The problem is when I call it:

    InitializeFileDialog(ref dialog);

error CS1503: Argument 1: cannot convert from 'ref Microsoft.Win32.OpenFileDialog' to 'ref Microsoft.Win32.FileDialog'

I tried to cast, but it couldn't for some reason. What's the problem? Is it because FileDialog is abstract? I tried to look up if that's the reason but I couldn't find anything useful.

Here are the declarations that are found in Microsoft.Win32:

public abstract class FileDialog : CommonDialog
public sealed class OpenFileDialog : FileDialog

I also tried to use generics and it didn't work. What am I missing?

Upvotes: 4

Views: 424

Answers (2)

Botz3000
Botz3000

Reputation: 39650

The best solution is to remove the ref keyword. It is really not needed in this case.

You only need ref, if your method should be able to reassign the variable that you passed in, which should be almost never. Returning a value, or working with the object directly as you are, is usually enough.

The compiler error however, comes from the fact that you need to pass in a variable of the exact type required by the method. Like this:

FileDialog baseDialog = fileDialog;
// baseDialog might be a different object when this returns
InitializeFileDialog(ref baseDialog); 

After all, the original variable that is passed as ref parameter could be reassigned by your method.

Now what would happen if it would assign a SaveFileDialog to your original variable of type OpenFileDialog? The world as we know it would end. That's why you need to create a temporary variable of type FileDialog. The type system allows your method to assign an object of any derived class to that variable.

See this blog entry by Eric Lippert for more info on this interesting topic:
Why do ref and out parameters not allow type variation?

Anyway: Don't use ref in this case.

Upvotes: 4

Kevin Gosse
Kevin Gosse

Reputation: 39027

The problem is with the ref keyword. It allows you to replace the caller's reference.

For instance:

static void Main(string[] args)
{
    string str = "hello";

    SomeFunction(ref str);

    Console.WriteLine(str); // outputs "world"
}

static void SomeFunction(ref string obj)
{
    obj = "world";
}

Now imagine you could use the base class, you'd have this situation:

static void Main(string[] args)
{
    OpenFileDialog dialog = new OpenFileDialog();

    SomeFunction(ref dialog);
}

static void SomeFunction(ref FileDialog obj)
{
    obj = new SaveFileDialog();
}

The code in SomeFunction is valid since SaveFileDialog is a FileDialog. But then, in the Main function, you would be assigning a SaveFileDialog to a OpenFileDialog, which is impossible. That's why, when using the ref keyword, you have to provide a reference of the exact same type.

To solve the issue in your case, simply remove the ref keyword.

Upvotes: 0

Related Questions