我可以通过引用传递对象

时间:2013-06-22 14:27:38

标签: c# reflection ref

我正在尝试将xna游戏中的对象传递到我正在编写的xna游戏库中的方法中。该对象基本上可以是任何类型,包括引用和值类型。

游戏库用于调试目的,应该向屏幕打印传入对象的当前值。它目前通过调用对象上的ToString来完成此操作,但将来字符串的格式将取决于在基础类型上。

以下是接收参数的游戏库方法

    public void AddToDebug(object obj)
    {
        var type = obj.GetType();
        _debugTypes.Add(obj, type);
    }

以下是在主游戏项目中使用它的示例

VD.AddToDebug(_counter);
VD.AddToDebug(_message);

_counter是一个int,_message是一个字符串。

问题是这些值的变化不会反映在屏幕上,因为(我假设)它们是按值传递的,而不是通过引用传递的。

我尝试添加ref关键字以确保它们通过引用传递但在方法调用中导致错误The ref argument type does not match parameter type

有没有办法可以通过引用传递值类型而不必指定实际类型(如果可以避免的话,我不想为所有值类型创建方法重载)。

还欢迎任何其他建议。 感谢

更新

在尝试了几种不同的方法后,我最终确定了将Func<T>传递给AddToDebug方法的简单方法,如下所示。我意识到,至少在目前,我不需要基础值,只需要格式化字符串表示。

将来我可以扩展库以自动扫描传入的类型并显示其所有成员的值。如果是这样,我可能会使用@Hans Passant的建议来传递对相关课程的引用。

那说我会接受@Lee的答案,因为它在我的最终实施中最有用,建议使用Func

这是我现在的代码,欢迎任何改进建议。 谢谢大家的帮助。

    public void AddToDebug<T>(Func<T> getValue, Color? color = null)
    {
        var w = new DebugEntry<T>(getValue, color.HasValue ? color.Value : Color.White);
        _values.Add(w);
    }

    public class DebugEntry<T> : IStringFormatter
    {
        private readonly Func<T> _getValue;
        public Color Color { get; private set; }

        public DebugEntry(Func<T> getValue, Color color)
        {
            _getValue = getValue;
            Color = color;
        }

        public string ToFormattedString()
        {
            return _getValue.Invoke().ToString();
        }
    }

    public interface IStringFormatter
    {
        string ToFormattedString();
        Color Color { get; }
    }

    // Example usage
    VD.AddToDebug(() => _counter);
    VD.AddToDebug(() => String.Format("This message says {0} times", _message));

3 个答案:

答案 0 :(得分:5)

您的int不在堆上,因此您无法创建永久引用。 ref引用仅在被调用函数返回之前存在(以保持内存安全)。您想要的是创建对存储int位置的引用。您最好的选择可能是创建一个包装类并在堆上实例化它:

class Box<T> { public T Value; }

您可以将引用传递给该类的实例,并且每个拥有该引用的人都可以读取和写入该值。

答案 1 :(得分:2)

  

_counter是一个int,_message是一个字符串。

哪种.NET类型会破坏您的计划。 int 是一种值类型,当您将其传递给采用类型 object 的参数的方法时,int将装箱。您将获得该值的副本。无论你做什么,更新该副本都不会对原始值产生影响。

string 类似的故事,它是不可变的引用类型。它无法以任何方式更改,您只能从原始对象创建 new 字符串对象。原始代码当然不知道的新字符串。

让更新在其他代码中可见需要指针。指针非常棘手,C#支持它们,但创建指向变量的指针有很多熊陷阱。例如,您可以永远不会创建指向局部变量的指针,并期望指针在方法退出时仍然有效。局部变量不再存在。无论如何通过指针更新值将损坏内存。使用指针时需要使用 unsafe 关键字的一个重要原因。

指针已经巧妙地包装在C#中,它们被称为引用。并在创建引用类型的对象时自动使用。所以你应该做的不只是传递简单的值或字符串,你应该传递对象的引用。一个类对象。现在,您的调试代码和原始代码都使用对完全相同对象的引用。对象的字段和属性所做的更新在另一个中是可见的。

答案 2 :(得分:1)

听起来你想要一种方法来包装变量而不是获取它的当前值。

您可以创建一个包装器,每次调用ToString时都会获取当前值,然后从包含的类中传递一个委托来获取变量:

public class Wrapper<T>
{
    private readonly Func<T> getter;
    public Wrapper(Func<T> getter)
    {
        this.getter = getter;
    }

    public override string ToString()
    {
        return getter().ToString();
    }
}

然后在你班上:

AddToDebug(new Wrapper<int>(() => this.intField));