【问题标题】:Troubles with LINQ and object referencesLINQ 和对象引用的问题
【发布时间】:2013-03-31 20:51:00
【问题描述】:

我一直在尝试按照以下方式进行工作,这是一个完整的代码示例,可以在 Visual Studio 中运行,它演示了我正在谈论的场景。这里的一切都按预期工作,当我更改播放器的 IsReady 值时,附加到事件的 lambda 被触发并且 if (to) 评估为 true。但是 Console.WriteLine 永远不会被命中,因为 ConcurrentBag 中播放器的 IsReady 值似乎没有更新。

class Program
{
    public static ConcurrentBag<Player> Players { get; set; }
    static void Main(string[] args)
    {
        Players = new ConcurrentBag<Player>();
        Player player = new Player() { Id = "123" };
        Players.Add(player);
        player.IsReady.ValueChanged += (from, to) =>
        {
            if (to)
            {
                if (Players.All(p => p.IsReady.Value))
                {
                    Console.WriteLine("It worked");
                }
            }
        };

        LookupPlayerById("123").IsReady.Value = true;
    }

    public static Player LookupPlayerById(string clientId)
    {
        var player = Players.FirstOrDefault(x => x.Id == clientId);
        return player;
    }
}

public class Player
{
    public string Id { get; set; }
    public MonitoredValue<bool> IsReady { get; set; }

    public Player()
    {
        IsReady = new MonitoredValue<bool>(false);
    }
}

public class MonitoredValue<T>
{
    public delegate void ValueChangedHandler(T from, T to);
    public event ValueChangedHandler ValueChanged;

    private T m_Value;
    public T Value
    {
        get { return m_Value; }
        set
        {
            if (ValueChanged != null) // if invocation list is not empty, fire the event
            {
                ValueChanged(m_Value, value);
            }
            m_Value = value;
        }
    }

    public MonitoredValue() { }

    public MonitoredValue(T initialValue)
    {
        m_Value = initialValue;
    }
}

【问题讨论】:

  • Playerclass 还是 struct?
  • 然后你返回一个 reference 给一个 Player 对象。更改它的状态将影响同一个对象,即来自ConcurrentBag&lt;Player&gt; 的链接的存储内容
  • 如果您知道使用 C# 和 LINQ 执行此操作的方法,请分享。那是我尝试做的第一件事,但这似乎是技术的限制?我必须以某种方式使用吗?我的印象是返回 FirstOrDefault() 会阻止您使用引用
  • IsReady是什么类型?
  • if (Players.All(p =&gt; p.IsReady.Value))

标签: c# linq collections


【解决方案1】:

问题是您首先通知您的听众,然后实际更改值。 将您的属性定义更改为下面给出的代码:

(delegate {}如果默认事件实现,现在我不需要在每次调用之前检查null)

public event ValueChangedHandler ValueChanged = delegate {};

public T Value
{
    get { return m_Value; }
    set
    {
        //first change
        m_Value = value;

        //now notify
        ValueChanged(m_Value, value);

    }
}

【讨论】:

  • 嗯,我刚刚运行了您的代码,发现它的行为符合预期。也许我缺少的 MonitoredValue 发生了一些事情
  • 请查看我更新的代码示例以查看重现问题的完整且包含的解决方案
  • @JesseCarter 解决了您的问题。对我来说,它打印了"It worked"
  • 哦,天哪,我不敢相信我错过了...非常感谢您的帮助,我正疯狂地试图弄清楚我错过了什么。
  • @JesseCarter 没关系,我们都会犯这些错误。我发现单元测试对减少此类错误的数量有很大帮助。
【解决方案2】:

由于是引用类型,所以Player会被FirstOrDefault返回的对象引用,除非是Default,这样你就没有匹配到。

示例演示:

    public static void Main()
    {
        ConcurrentBag<ClassA> test = new ConcurrentBag<ClassA>();
        var hurp = new ClassA();
        hurp.number = 3;
        test.Add(hurp);

        var derp = test.FirstOrDefault();
        derp.number = 4;

        Console.Write(test.FirstOrDefault().number);
        Console.WriteLine(derp.number);
        Console.ReadLine();
    }

打印: 44

【讨论】:

  • 类的“默认”是null,所以他的代码在这种情况下会崩溃,你的代码也会崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-03
  • 2017-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多