11

Can someone explain to me why this is incorrect in C#:

namespace NamespaceA
{
    public class ClassA
    {
        public interface IInterfaceA
        {
            String Property
            {
                set;
            }
        }
    }
}

namespace NamespaceB
{
    public class ClassB
    {
        public class ImpA: NamespaceA.ClassA.IInterfaceA
        {
            private String mProperty;
            public String Property{ set{ mProperty = value; } }
        }
        public ClassB()
        {
            ImpA aImpA = new ImpA();
            foo(ref aImpA);
        }

        private void foo(ref NamespaceA.ClassA.IInterfaceA aIInterfaceA)
        {
            aIInterfaceA.Property = "SomeValue";
        }
    }
}

This will produce a compile error of:

Error Argument 1: cannot convert from 'NamespaceB.ClassB.ImpA' to 'ref NamespaceA.ClassA.IInterfaceA'

It seems perfectly reasonable to want to modify the interface properties and call the interface functions from foo(). If you remove the ref keyword, it compiles, but the changes you make in foo() are lost...

JHowIX
  • 1,683
  • 1
  • 20
  • 38
  • 2
    what? you don't need ref in order to manipulate properties from an object. – Federico Berasategui Feb 08 '13 at 02:20
  • Have to agree with @HighCore - ref is totally unnecessary here. – JerKimball Feb 08 '13 at 02:25
  • 1
    I edit the title from *reference* to `ref`. C#, like Java and Python, passes objects using [*Call By Object Sharing*](http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing) semantics. This is unrelated to *Call by Reference* (which is what `ref` does). See the previous comments. –  Feb 08 '13 at 02:27

2 Answers2

16

As Karthik said, ref and out don't support object-oriented polymorphism. But you can use generics (a.k.a. parametric polymorphism) to achieve the same effect.

Try changing the signature of foo to:

private void foo<T>(ref T aIInterfaceA) 
    where T : NamespaceA.ClassA.IInterfaceA
{
    aIInterfaceA.Property = "SomeValue";

    // This assignment will be visible to the caller of foo
    aIInterfaceA = default(T);
}

Bonus — If you want, you can put a new() constraint on the type parameter T and then it even lets you call its default constructor:

private void foo<T>(ref T aIInterfaceA) 
    where T : NamespaceA.ClassA.IInterfaceA, new()
{
    aIInterfaceA.Property = "SomeValue";

    // This assignment will be visible to the caller of foo
    aIInterfaceA = new T();
}
Saintali
  • 4,482
  • 2
  • 29
  • 49
0

Firstly, there is no need to use the ref keyword there.

You're passing an instance of reference type as a parameter and you do not need to mark the argument as ref to be able to modify its state, here Property property. Just remove the ref keyword and it'll work as expected.

Secondly, just think it over. As soon as an instance of the interface is a reference type, ref parameter makes it possible to change the reference passed, so you theoretically can return an absolutely different implementation of this interface.

So, definetely there is no implicit cast from IInterfaceA to ImpA, while your code requires the one.

horgh
  • 17,918
  • 22
  • 68
  • 123