【问题标题】:Calling methods or properties of objects in List调用List中对象的方法或属性
【发布时间】:2013-10-28 22:10:08
【问题描述】:

我正在我的 XNA 游戏中编写一个调试窗口。这只是一个打印出东西的StringBuilder。但是我想让它更具交互性。例如Player 类:

public class Player
{
   public Vector2 Position { get; set; }
   public int Health { get; set; }
}

我想要一个要调用的属性(或方法)列表。所以像:

list.Add(player1.Position);
list.Add(player1.Health);
list.Add(enemy2.Position);

因此,每次更新时,列表都会调用这些属性并打印它们的值。有没有办法做到这一点?我需要使用反射吗?使用方法而不是属性会有很大不同吗?

编辑:这是我目前拥有的 https://github.com/nanexcool/xna-debug-window/blob/master/DebugWindow.cs

它通过将对象和属性名称指定为字符串来工作,但可能有更好的方法。

【问题讨论】:

  • 您能否准确地提供您希望调试窗口的工作方式?可能类似于 Visual Studio 中的 Watch 窗口?
  • 是的,您可以使用反射来获取当前项目的所有属性名称及其值。只是做一些阅读。
  • 这是我在屏幕上绘制的窗口。基本上只是遍历对象,调用某些属性,并显示它们的 .ToString()
  • 这是我目前所拥有的,一个 Dictionary。我添加了一个播放器 .Add(player1, "Position")。然后我通过遍历字典来获取数据: string data = s.Key.GetType().GetProperty(s.Value).GetValue(s.Key, null).ToString();似乎有效,可能有更简单的方法。

标签: c# list xna


【解决方案1】:

使用属性名称和返回该属性的委托创建一个元组(或字典)列表:

using DebugTuple = Tuple<string, Func<string>>;

class Program
{  
    class Player
    {
        public int x;
        public Player(int y) { x = y; }            
    }

    static void Main(string[] args)
    {    
        Player one = new Player(25);
        Player two = new Player(50);

        List<DebugTuple> calls = new List<DebugTuple>();
        calls.Add(new DebugTuple("Player 1 health", delegate() { return one.x.ToString(); })); 
        calls.Add(new DebugTuple("Player 2 health", delegate() { return two.x.ToString(); })); 

        foreach (DebugTuple c in calls)
            Console.WriteLine(c.Item1 + ": " + c.Item2());

        //Change values and make sure it outputs new values
        one.x = 100;
        two.x = 0;

        foreach (DebugTuple c in calls)
            Console.WriteLine(c.Item1 + ": " + c.Item2());

        Console.ReadLine();

    }

}

输出:

Player 1 health: 25
Player 2 health: 50
Player 1 health: 100
Player 2 health: 0

【讨论】:

  • 这几乎正是我所需要的。谢谢!我将元组更改为 Tuple>,所以我可以只返回 one.x,然后在 c.Item2().ToString() 上执行 ToString。会试试的。
  • 没问题。只是一个警告:当你创建一个引用一个对象的委托时,当它超出范围时垃圾收集器将不再删除该对象(因为它仍在委托的范围内)。如果您将创建大量对象(或非常大的对象)并且内存使用是一个问题,您应该想出一些方法来确保在删除一个对象时,您也会删除所有引用它的委托。跨度>
【解决方案2】:

也许有人会发现这也很有用:

/// <summary>
/// Represents your player class.
/// </summary>
class Player
{
    /// <summary>
    /// Gets or sets the health of the player.
    /// </summary>
    [DebugExtensions.DebugMePlease()]
    public int Health { get; set; }

    /// <summary>
    /// Gets or sets the name of the player.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Gets a string representation of the player object.
    /// </summary>
    /// <returns>The player object as string representation.</returns>
    public override string ToString()
    {
        return this.Name;
    }
}

/// <summary>
/// Contains some extensions useful for debugging.
/// </summary>
public static class DebugExtensions
{
    /// <summary>
    /// Represents our custom attribute called "DebugMePlease".
    /// Properties marked with this attribute will be printed automatically.
    /// </summary>
    public class DebugMePlease : Attribute
    {
    }

    /// <summary>
    /// Continas all objects that shall be monitored.
    /// </summary>
    public static List<object> DebugList
    {
        get;
        set;
    }

    /// <summary>
    /// Initializes static members of the <see cref="DebugExtensions"/> class.
    /// </summary>
    static DebugExtensions()
    {
        DebugList = new List<object>();
    }

    /// <summary>
    /// Prints the values of all objects in the debugList.
    /// </summary>
    public static void Print()
    {
        foreach (object o in DebugList)
        {
            var members = from member in o.GetType().GetProperties()
                          from attribute in member.GetCustomAttributes(typeof(DebugMePlease), true)
                          select member;

            foreach (var z in members)
            {
                Console.WriteLine(string.Format("{0}, {1}: {2}", o.ToString(), z.Name, z.GetValue(o)));
            }
        }
    }
}

/// <summary>
/// Contains the entry point of our application.
/// </summary>
public class Program
{
    /// <summary>
    /// The entry point of our application. 
    /// </summary>
    /// <param name="args">Possibly specified command line arguments.</param>
    public static void Main(string[] args)
    {
        Player t = new Player();
        t.Name = "Chuck Norris";
        t.Health = int.MaxValue; // Becaus it's Chuck Norris ;-)

        // Add the object to the debug list.
        DebugExtensions.DebugList.Add(t);

        // Print all properties marked with the "DebugMePlease" attribute.
        DebugExtensions.Print();

        // Change something.
        t.Health = 0;

        // Print again and hopefully be happy.
        DebugExtensions.Print();

        Console.ReadLine();
    }
}

【讨论】:

    【解决方案3】:

    也许我误解了一些东西,但如果您已经有一个包含引用的列表,您不能使用 .ToString() 打印它们吗? (当然,您必须在自己的类中覆盖 ToString()。)

    【讨论】:

    • 问题是如果我将 player1.Position 或enemy.Health 直接添加到列表中,添加的是值并且不会更新。
    • 那是因为你存储了值类型。如果你只存储引用类型当然没有问题。
    • 我知道!但是我存储Vector2等值类型,然后调用属性看看有没有变化。
    • 您可以在数据结构中实现属性更改事件,并在每次属性更改时触发该事件(或使用 INotifyPropertyChanged)。然后,您只需从外部连接到事件并始终接收当前值。
    • INotifyPropertyChanged 听起来不错,但这意味着修改我想从中获取信息的每个类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-17
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多