Click to See Complete Forum and Search --> : Question about method with ref argument


Grofit
May 16th, 2009, 09:40 AM
Hey,

Hopefully a simple question, heres some code:



class A { public int Var1; }
class B : A { public int Var2; }

class C
{
public static A GetA()
{
A Var = new A();
generateA(Var);
return Var;
}

public static A GetB()
{
A Var = new B();
generateA(ref Var);
generateB(ref Var); // Error
return Var;
}

protected static generateA(ref A Var)
{ Var.Var1 = 1; }

protected static generateB(ref B Var)
{ Var.Var2 = 2; }

}



Now thats just a simple example of what im trying to do, but it seems to kick up a fuss when i try to pass over a base type that is acctually a subclass. In my real code i have a few methods that build up classes internal members, so one method for each sub class, that way i can reuse the code to build up the subclasses by populating in order...

Anyway hopefully that shows what im trying to do, any help or advice would be great!

BigEd781
May 16th, 2009, 03:16 PM
Well, there are a couple of problems. A is not a B, so you can't pass it in its place. You can pass in a B for an A, but the other way around does not make sense. It has nothing to do with the ref specifier. Even though the A is a B under the hood, you cannot implicitly convert and A to a B, so you must cast it.

Also, you have to pass them in using the 'ref' keyword or it will not compile.

Grofit
May 17th, 2009, 05:26 AM
Oh sorry that was a typo on my behalf, will update the previous post.

The whole point of polymorphism is that you have a shared base type but cast it at runtime to be an inherited type. So there shouldnt be problem casting from A -> B, providing it is an instance of B within the A object.

If i remove the refs and do a manual cast it works fine, but then any updates i would do to the object wouldnt persist. I would make it of type B to begin with if i could, but then the first method would kick up a fuss saying that B isnt an A object, which is stupid as of course B is of type A as it inherits from it...

So im thinking that ref handles objects differently, maybe i could cast it from A to whatever object it needs when im within the method...

Mutant_Fruit
May 17th, 2009, 12:12 PM
class A { public int Var1; }
class B : A { public int Var2; }

class C
{

public static A GetB()
{
A Var = new B();
generateB(ref Var); // Error
return Var;
}
protected static generateB(ref B Var)
{ Var.Var2 = 2; }
}


What that method call is trying to say is that an 'A' *is a* 'B'. However this is not true, the opposite is true. You can't pass an 'A' in the place of a 'B' argument. It's like trying to feed a steak to a vegetarian. It just doesn't won't work ;)

Sure, in this exact case it can be statically determined that Var is actually a 'B', but as you've declared it of type 'A' and tried to pass it to an argument of incompatible type 'B', you fail. The C# compiler doesn't allow you to do (broken) stuff like this. It's a good compiler ;)

Grofit
May 17th, 2009, 02:47 PM
Its technically not broken though, i can see why it does it, but if i were to do the following:


class A { public int Var1; }
class B : A { public int Var2; }

class C
{
public static A GetA()
{
A Var = new A();
generateA(Var);
return Var;
}

public static A GetB()
{
B Var = new B();
generateA(ref Var); // Error
generateB(ref Var);
return Var;
}

protected static generateA(ref A Var)
{ Var.Var1 = 1; }

protected static generateB(ref B Var)
{ Var.Var2 = 2; }

}


I get the same error just the other way round on the generateA() function. It would be fine if it allowed me to pass in a B as an A, but neither way works so im a bit stumped as to how i can build up different parts of a class this way, or im going to have to duplicate the generateA() code into the generateB/C/D/E methods etc...

I can understand that B object *is not an* A object, however it is 100% guarenteed that B *does have* the same members as an A object, so why does it kick up a fuss in this regard?

BigEd781
May 17th, 2009, 05:03 PM
Oh sorry that was a typo on my behalf, will update the previous post.

The whole point of polymorphism is that you have a shared base type but cast it at runtime to be an inherited type.

I wouldn't say that is "the whole point of polymorphism", but I know what you are getting at.

Anyway, your second example will work just fine. A "B" can be passed to a function that takes an "A" and, through the "A" class interface, will still act like a B. Look at this program:


class A
{
public virtual string Name
{
get
{
return "I'm an A";
}
}
}

class B : A
{
public override string Name
{
get
{
return "I'm a B";
}
}
}

class Program
{
static void Main( string[ ] args )
{
B aB = new B( );
A anA = new A( );
PrintA( aB ); // workfs fine, prints "I'm a B".
PrintB( anA ); // ERROR! A is *not* a B, and it cannot be converted to one.
}

static void PrintA( A a )
{
Console.WriteLine( a.Name );
}

static void PrintB( B b )
{
Console.WriteLine( b.Name );
}
}


It *does* work. That is polymorphism, because the "B" still acts like a "B", but the function need only know that a "B" is a descendant of an "A". The other will not work. What if you add a new method to class "B" and try to call that method on something that is actually an "A"? That would not be possible, so there is no logical way to convert in that direction.

Also, the "ref" argument is not needed. When you use the "ref" keyword, you are passing in the actual reference to the object, not a copy of one (C# passes references by value by default). So, unless you are changing what the referents points to, you do not need to use the "ref" keyword on reference types.

Mutant_Fruit
May 17th, 2009, 05:35 PM
class A { public int Var1; }
class B : A { public int Var2; }

class C
{
public static A GetB()
{
B Var = new B();
generateA(ref Var);
}

protected static generateA(ref A Var)
{
Var = new A ();
}
}


If you were allowed pass a 'B' location in this scenario, you can then put an 'A' where an 'A' cannot possibly exist. It's the direct equivalent of:

B b = new A ();

Which is obviously not allowed.

Grofit
May 18th, 2009, 03:15 PM
I wouldn't say that is "the whole point of polymorphism", but I know what you are getting at.

Anyway, your second example will work just fine. A "B" can be passed to a function that takes an "A" and, through the "A" class interface, will still act like a B. Look at this program:


class A
{
public virtual string Name
{
get
{
return "I'm an A";
}
}
}

class B : A
{
public override string Name
{
get
{
return "I'm a B";
}
}
}

class Program
{
static void Main( string[ ] args )
{
B aB = new B( );
A anA = new A( );
PrintA( aB ); // workfs fine, prints "I'm a B".
PrintB( anA ); // ERROR! A is *not* a B, and it cannot be converted to one.
}

static void PrintA( A a )
{
Console.WriteLine( a.Name );
}

static void PrintB( B b )
{
Console.WriteLine( b.Name );
}
}


It *does* work. That is polymorphism, because the "B" still acts like a "B", but the function need only know that a "B" is a descendant of an "A". The other will not work. What if you add a new method to class "B" and try to call that method on something that is actually an "A"? That would not be possible, so there is no logical way to convert in that direction.

Also, the "ref" argument is not needed. When you use the "ref" keyword, you are passing in the actual reference to the object, not a copy of one (C# passes references by value by default). So, unless you are changing what the referents points to, you do not need to use the "ref" keyword on reference types.

Hey,

Yeah, ive changed over to not using ref and works fine. However i thought it would work with ref anyway, but thats where my problems were coming from, trying to pass the reference type as a ref, but anyway problems solved i guess...

BigEd781
May 18th, 2009, 04:15 PM
The problem was not caused in any way by the 'ref' keyword, it would have worked just the same (but it was not needed).

Mutant_Fruit
May 19th, 2009, 04:34 PM
Hey,

Yeah, ive changed over to not using ref and works fine. However i thought it would work with ref anyway, but thats where my problems were coming from, trying to pass the reference type as a ref, but anyway problems solved i guess...
At least you now understand *why* you couldn't pass it by ref. So you've learnt something new out of it aswell :) Though yes, 'ref' is completely unnecessary in this scenario.