2

我正在尝试将 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

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

也欢迎任何其他建议。谢谢

更新

在尝试了几种不同的方法后,我最终确定了将 a 传递给方法的简单方法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));
4

3 回答 3

5

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

class Box<T> { public T Value; }

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

于 2013-06-22T14:31:28.737 回答
2

_counter 是一个 int 并且 _message 是一个字符串。

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

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

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

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

于 2013-06-22T14:43:37.180 回答
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));
于 2013-06-22T14:35:23.240 回答