4

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?

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
MasterMastic
  • 20,711
  • 12
  • 68
  • 90
  • 3
    Remove the `ref` keyword. Objects are passed by reference, so you don't need it in the above example. – adrianbanks Dec 19 '12 at 12:56
  • @adrianbanks It solved the problem! thank you. But why it threw an error? – MasterMastic Dec 19 '12 at 12:57
  • @adrianbanks is correct, but it is worth noting that `ref` allows you to assign a different object to the passed variable, so in some cases is useful when passing an object. Assigning `null` to an object variable won't change the original object outside of the function scope, for example. See http://stackoverflow.com/questions/186891/why-use-ref-keyword-when-passing-an-object for more information. – Levi Botelho Dec 19 '12 at 13:02

2 Answers2

4

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.

Botz3000
  • 39,020
  • 8
  • 103
  • 127
  • Removing `ref` worked, but I'm trying to find out why did the compiler threw an error. I couldn't even cast it (as you mentioned first). – MasterMastic Dec 19 '12 at 13:00
  • @Ken It's because when a parameter is passed as `ref`, the method can reassign the **original** variable. When you just cast another variable, this doesn't create a new variable to store the new value in once it is reassigned. – Botz3000 Dec 19 '12 at 13:02
  • Oh now I see! kinda expected the compiler to be clearer about that. Anyways, thanks a lot! spending most of my time in C++ I just can't get that classes are references with no operator like that. – MasterMastic Dec 19 '12 at 13:06
  • This answer could be improved with an example using self-defined classes. Just a comment – Alvin Wong Dec 19 '12 at 13:10
  • @AlvinWong I think Save- and OpenFileDialog are fine as an example. But i improved the answer with a link to a very relevant blog post :) – Botz3000 Dec 19 '12 at 13:15
0

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.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94