0

Maybe what I want to do is just the wrong way of going about it. But i am trying to write a game debug printer thing. It is to write values to the screen in a sprite font for various things i tell the debugger to care about. My debugger class is simple , but it seems that in c# things are getting passed by value not reference. So I turn to the ref keyword, which seems to work okay if I pass in the right data type. The issue is that I have this method signature:

Dictionary<String, object> debugStrings = new Dictionary<String, object>();
...
...

public void addVariable(String index, ref object obj) //i think it needs to be a reference, not so sure though.
    {
        debugStrings.Add(index, obj);                        
    }

This adds to the dictionary a variable to ultimately print to the screen along with a key for it to be known as.

The problem though arises when I try to use the above method:

debugPrinter.addVariable("myrotationvalue", ref this.Rotation);

as per the link in the comment I changed the code above to:

this.Rotation = 4;
object c = (object)this.Rotation;
this.Rotation = 20;
level.dbgd.addVariable("playerrot", ref c);

//always prints 4 out, i guess it still is not passing by reference?

So it doesn't give an error but always prints out 4. Not sure how I will ever get the reference to work.

Again on another edit took the references out:

this.Rotation = 20;
level.dbgd.addVariable("playerrot", this.Rotation);
this.Rotation = 4;  //should draw to screen 4, doesn't draws 20

I apparently this is harder than I thought it would be a simple little fun class to work up before I hit the hay, ha ha ha... this is not boding well for the monday to come.

Full class:

namespace GameGridTest.GameGridClasses.helpers
{
public class DebugDrawer : DrawableGameComponent
{
    Dictionary<String, object> debugStrings = new Dictionary<String, object>();
    int currentX;
    int currentY;
    VictoryGame _game;
    private SpriteBatch spriteBatch;
    private SpriteFont spriteFont;
    public DebugDrawer(VictoryGame game) : base(game)
    {
        _game = game;
        currentX = _game.Window.ClientBounds.Width - 400; //draw the debug stuff on right side of screen
        currentY = 5;


    }






    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        spriteFont = _game.Content.Load<SpriteFont>("fonts\\helpers\\fpsFont");
    }
    public void addVariable<T>(String index, AbstractDebugHandler<T> obj) //i think it needs to be a reference, not so sure though.
    {
        debugStrings.Add(index, obj);                        
    }
    public void removeVariable(String index)
    {
        debugStrings.Remove(index);                        
    }
    protected override void UnloadContent()
    {
        _game.Content.Unload();
    }
    public override void Update(GameTime gameTime)
    {           
    }
    public override void Draw(GameTime gameTime)
    {         
        spriteBatch.Begin();
        foreach (object obj in debugStrings) //draw all the values
        {
            spriteBatch.DrawString(spriteFont, obj.ToString(), new Vector2(currentX, currentY), Color.White);
            currentY += 30;
        }
        currentY = 5;
        spriteBatch.End();
    }
}

public abstract class AbstractDebugHandler<T>
{

    public AbstractDebugHandler(T obj)
    {
        InnerObject = obj;
    }

    public T InnerObject { get; private set; }

    public abstract string GetDebugString();

}
public class ThisDebugHandler: AbstractDebugHandler<object>{
    public ThisDebugHandler(object innerObject) : base(innerObject){
    }

    public override GetDebugString(){
        return InnerObject.Rotation; //??

    }
  }
}
Codejoy
  • 3,722
  • 13
  • 59
  • 99
  • possible duplicate of [method with ref object parameter](http://stackoverflow.com/questions/2295532/method-with-ref-object-parameter) – V4Vendetta Dec 12 '11 at 07:22
  • you are right. Edited the question for a new issue I have since you passed the link....ty – Codejoy Dec 12 '11 at 07:26
  • try passing "this" and using .Rotation in the inner Method, it sould work fine! The Problem is, that you are always "copy" Rotation by assigning it, because it is a ValueType. ValueTypes can't be referenced. Please take a look at http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx – oberfreak Dec 12 '11 at 07:38

2 Answers2

2

You are confusing passing a reference and passing by reference.

It's true that parameters are passed by value, but that doesn't mean that you can't pass a reference type. The reference is passed by value, which means that the reference is copied to the stack, the object itself is not copied.

Passing by reference is used when you have to change the variable that you are passing in.

Just remove the ref keyword, and your code will work just fine.


Edit:

As you are passing a value type into the method, it will be boxed, so the value is actually copied. You will be displaying the copy of the value, not the live value. Passing the parameter by reference doesn't help either, as you have to copy the value to make an object reference to it.

If you want to display value types, you would rather send in a function that can retrieve the value instead of the value itself:

Dictionary<String, Func<object>> debugStrings = new Dictionary<String, Func<object>>();

public void addVariable(String index, Func<object> getValue) {
  debugStrings.Add(index, getValue);                        
}

Usage:

debugPrinter.addVariable("myrotationvalue", () => this.Rotation);
Community
  • 1
  • 1
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
1

Ref is giving the pointer of the variable to the other Method, this means that the other method can set the value of the variable and not only use the pointer to the object. This is verry serious! Take a look at following code:

public void Test(){
    object a = new object();
    Test(a);
    if(a==null)
        Debug.WriteLine("isNull");
    else
        Debug.WriteLine("isSet");
}
public void Test2(ref object obj){
    obj = null;
}

You can have the same behavior without this passing the variable by ref. Box the valueTypes in a object so the pointer to the object can be stored and not the pointer to the variable.

For Example:

public abstract class AbstractDebugHandler<T>{

    public AbstractDebugHandler(T obj){
        InnerObject = obj;
    }

    public T InnerObject {get; private set;}

    public abstract string GetDebugString();

}

public void addVariable<T>(String index, DebugHandler<T> obj) 
{
    debugStrings.Add(index, obj);                        
    //to get debugString use
    string debugValue = obj.GetDebugString();
    // will always geht the current Value of the defined Object
}

In your case you would have to set "this" as the innerObject, because Rotation is a ValueType and you have to pass the Object containing the ValueType so both Methods "Debugger" and "callingMethod" are working on the Same Object "this". The DebugHandler Class can now handle the string conversion. See

public class ThisDebugHandler: AbstractDebugHandler<ThisType>{
    public ThisDebugHandler(ThisType innerObject) : base(innerObject){
    }

    public override GetDebugString(){
        return InnerObject.Rotation;
    }
}

So if you would now call your debug method like:

public void MainMethod(){
    this.Rotation = 4;
    ThisDebugHandler handler = new ThisDebugHandler(this);
    level.dbgd.addVariable<ThisType>("someIndex",handler );
    this.Rotation = 20;

    //level.dbgd.print();
    // now prints 20
}

Would be glad helping you, so please ask if you have further questions

oberfreak
  • 1,799
  • 13
  • 20