5

I have a method with this signature:

protected bool MyMethod (ref IMyInterface model) {
    // stuff and things
}

I have a model that I'm passing in from this class:

public class MyClass: IMyInterface {
    // class stuff and things
}

I'm trying to pass my model into the method like so:

var model = new MyClass():

MyMethod(ref model);

However, I get an error about the type not matching the parameter type. If I don't pass by reference, it works fine. Or, if I cast it and pass it like so, it works fine.

var tempModel = (IMyInterface)model;

MyMethod(ref tempModel);

I'd rather avoid a cast if it's not necessary, but I can't pass without it. I thought if the class implemented the interface, I could pass the model. Is it just not a thing I can do by reference or am I missing something?

Yatrix
  • 13,361
  • 16
  • 48
  • 78
  • 2
    Silly question, but is it `ref` for a reason? The method might actually assign a new value to `model`, not just alter its fields? – Blorgbeard Mar 26 '15 at 00:29
  • @Blorgbeard It could probably be refactored, yes. It's one of those things I tried and couldn't do, so I was curious if it WAS possible and if not, maybe some insight as to why. – Yatrix Mar 26 '15 at 00:47

3 Answers3

18

If you don't use implicit typing, and just define your variable as the interface, it will work:

IMyInterface model = new MyClass():

MyMethod(ref model);

Arguments passed by ref must match the type exactly, as they can be reassigned within the method to another type that matches that contract. In your case, this won't work. Imagine the following:

protected bool MyMethod (ref IMyInterface model) 
{
    // This has to be allowed
    model = new SomeOtherMyInterface();
}

// Now, in your usage:
var model = new MyClass(); // Exactly the same as MyClass model = new MyClass();

MyMethod(ref model); // Won't compile...

// Here, model would be defined as `MyClass` but have been assigned to a `SomeOtherMyInterface`, hence it's invalid...
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • The interface doesn't have all of the members the class has, so when I declare it as the interface, I can't set the class-specific properties. Good info, though. I didn't want to go into all of that because the question I had was surrounding the ref with an interface param. Thanks, though. – Yatrix Mar 26 '15 at 00:24
  • @Yatrix You can assign to a separate variable, and pass that. If you're not reassigning, though, there's no reason to pass by ref in the first place... – Reed Copsey Mar 26 '15 at 00:45
6

ref cannot take a subtype of the requested type (ie. IMyInterface) because then the method cannot guarantee the assignment would not violate the caller (ie. it wanted a MyClass). As such using ref (or out) requires the exact type.

This is effectively what the error is:

MyClass m = new MyClass();
IMyInterface f = m; // Okay: "m is IMyInterface"

MyMethod(ref f);    // Okay
m = f;              // Illegal: can't guarantee "f is MyClass".

C# simply forbids such with ref, although with a bit of "help" manually..

m = (MyClass)f;     // "Trusting this is okay"

See https://stackoverflow.com/a/2877515/2864740 a more in-depth explanation on reasons for the variance rules of the out parameter keyword.

user2864740
  • 60,010
  • 15
  • 145
  • 220
3

You can achieve this with generics like so:

public static class Disposable
{
    public static void TryDisposeAndClear<T>(ref T obj) where T : IDisposable
    {
        try
        {
            obj.Dispose();
        }
        catch { }

        obj = default(T);
    }
}
Mike Marynowski
  • 3,156
  • 22
  • 32