我想在c#中使用ref传递值,但它不像我想的那样工作

本文关键字:工作 ref | 更新日期: 2023-09-27 18:17:46

也许我想做的只是错误的方法。但我正试图写一个游戏调试打印机的东西。它是用精灵字体将值写入屏幕,用于我告诉调试器要关心的各种事情。我的调试器类很简单,但在c#中,事情似乎是通过值而不是引用传递的。所以我转向ref关键字,如果我传递正确的数据类型,它似乎可以正常工作。问题是我有这个方法签名:

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);                        
    }

这将在字典中添加一个变量,最终打印到屏幕上,并为它添加一个键。

当我尝试使用上面的方法时,问题出现了:

debugPrinter.addVariable("myrotationvalue", ref this.Rotation);
根据注释中的链接,我将上面的代码更改为:
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?

所以它不会给出错误,但总是输出4。

我不知道我怎样才能把参考资料用起来。

再次在另一个编辑中删除了参考文献:

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

我显然这比我想象的要难,这将是一个简单的小有趣的课程,在我上床睡觉之前,哈哈哈…这对下周一来说不是好兆头。

完整

类:

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; //??
    }
  }
}

我想在c#中使用ref传递值,但它不像我想的那样工作

你混淆了传递一个引用和通过引用传递

形参确实是按值传递的,但这并不意味着不能传递引用类型。引用是按值传递的,这意味着引用被复制到堆栈中,对象本身没有被复制。

按引用传递在必须更改要传入的变量时使用。

只要去掉ref关键字,你的代码就可以正常工作了。


编辑:

当您将值类型传递给方法时,它将被装箱,因此该值实际上被复制。您将显示该值的副本,而不是实时值。通过引用传递形参也没有帮助,因为您必须复制该值以使对象引用它。

如果你想显示值的类型,你宁愿发送一个函数来检索值,而不是值本身:

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

用法:

debugPrinter.addVariable("myrotationvalue", () => this.Rotation);

Ref将变量的指针给了另一个方法,这意味着另一个方法可以设置变量的值,而不仅仅是使用指向对象的指针。这是非常严重的!看看下面的代码:

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;
}

你可以有相同的行为,而不是通过ref传递变量。在对象中设置valueTypes,这样可以存储指向对象的指针,而不是指向变量的指针。

例如:

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
}

在你的情况下,你必须设置"this"作为innerObject,因为旋转是一个ValueType,你必须传递包含ValueType的对象,所以两个方法"调试器"answers"callingMethod"都在同一个对象"this"上工作。DebugHandler类现在可以处理字符串转换。看到

public class ThisDebugHandler: AbstractDebugHandler<ThisType>{
    public ThisDebugHandler(ThisType innerObject) : base(innerObject){
    }
    public override GetDebugString(){
        return InnerObject.Rotation;
    }
}

所以如果你现在像这样调用你的调试方法:

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
}

我很乐意帮助你,如果还有问题请问我