保持对象之间的一对一映射

时间:2010-04-23 05:38:48

标签: c#

我有以下两个类,它们之间提供一对一的映射。我如何处理空值,当我运行第二个测试时,我得到一个stackoverflow异常。我怎样才能阻止这种递归循环?感谢

[TestMethod]
public void SetY()
{
    var x = new X();
    var y = new Y();

    x.Y = y;

    Assert.AreSame(x.Y, y);
    Assert.AreSame(y.X, x);
}

[TestMethod]
public void SetYToNull()
{
    var x = new X();
    var y = new Y();

    x.Y = y;
    y.X = null;

    Assert.IsNull(x.Y);
    Assert.IsNull(y.X);
}

public class X
{
    private Y _y;

    public Y Y
    {
        get { return _y; }
        set
        {
            if(_y != value)
            {
                if(_y != null)
                {
                    _y.X = null;
                }

                _y = value;

                if(_y != null)
                {
                    _y.X = this;
                }
            }
        }
    }
}

public class Y
{
    private X _x;

    public X X
    {
        get { return _x; }
        set
        {
            if (_x != value)
            {
                if (_x != null)
                {
                    _x.Y = null;
                }

                _x = value;

                if (_x != null)
                {
                    _x.Y = this;
                }
            }
        }
    }
}

4 个答案:

答案 0 :(得分:1)

工作正常:

public class ClassX
{
    private ClassY _Y;

    public ClassY Y
    {
        get { return _Y; }
        set
        {
            if (_Y != value)
            {
                var oldY = _Y;
                _Y = value;

                if (_Y == null)
                {
                    oldY.X = null;
                }
                else
                {
                    _Y.X = this;    
                }
            }
        }
    }
}

public class ClassY
{
    private ClassX _X;

    public ClassX X
    {
        get { return _X; }
        set
        {
            if (_X != value)
            {
                var oldX = _X;

                _X = value;
                if (_X == null)
                {
                    oldX.Y = null;
                }
                else
                {
                    _X.Y = this;    
                }

            }
        }
    }
}

答案 1 :(得分:1)

当设置y.X = null;时,会发生的事情是它会尝试将yX设置为null,因为_x不为null,而后者又尝试将(yX).Y设置为null,因为x中的_y仍然不为null然而......并且你得到了这个想法 - 一个无限循环。

我已更改它,以便在分配成员变量的属性之前先分配成员值。

public class X
{
    private Y _y;

    public Y Y
    {
        get { return _y; }
        set
        {
            if (_y != value)
            {
                Y temp = _y;

                _y = value;

                // If new value is not null
                if (_y != null)
                {
                    _y.X = this;
                }
                // If old value is not null but new value is 
                else if (temp != null)
                {
                    temp.X = null;
                }
            }
        }
    }
}

public class Y
{
    private X _x;

    public X X
    {
        get { return _x; }
        set
        {
            if (_x != value)
            {
                X temp = _x;

                _x = value;

                // If new value is not null
                if (_x != null)
                {
                    _x.Y = this;
                }
                // If old value is not null but new value is 
                else if (temp != null)
                {
                    temp.Y = null;
                }
            }
        }
    }
}

答案 2 :(得分:0)

你在这里遇到无限循环。

如果您不想获得null,请使用此选项:

get 
{ 
    if (_y == null)
        _y = new Y();
    return _y; 
}

答案 3 :(得分:0)

使用单独的实体来存储对象之间的关系。像这样:

[TestFixture]
    public class Tester
    {
        [Test]
        public void SetY()
        {
            var refs = new References();

            var x = new X(refs);
            var y = new Y(refs);

            x.Y = y;

            Assert.AreSame(x.Y, y);
            Assert.AreSame(y.X, x);
        }

        [Test]
        public void SetYToNull()
        {
            var refs = new References();

            var x = new X(refs);
            var y = new Y(refs);


            x.Y = y;
            y.X = null;

            Assert.IsNull(x.Y);
            Assert.IsNull(y.X);
        }
    }

    public class References
    {
        private IDictionary<X, Y> refs = new Dictionary<X, Y>();

        public bool Contains(X x, Y y)
        {
            if (!refs.ContainsKey(x)) return false;
            if (refs[x] != y) return false;

            return true;
        }

        public void Delete(X x)
        {
            refs.Remove(x);
        }

        public void Add(X x, Y y)
        {
            refs.Add(x, y);
        }

        public Y Get(X x)
        {
            return refs.ContainsKey(x) ? refs[x] : null;
        }

        public X Get(Y y)
        {
            var pairs = refs.Where(r => r.Value == y);

            return pairs.Any() ? pairs.FirstOrDefault().Key : null;
        }

        public void Delete(Y y)
        {
            X x = Get(y);

            if (x != null)
            {
                Delete(x);
            }
        }
    }

    public class X
    {
        private readonly References refs;

        public X(References refs)
        {
            this.refs = refs;
        }

        public Y Y
        {
            get { return refs.Get(this); }
            set
            {
                if (value == null)
                {
                    refs.Delete(this);
                }
                else
                {
                    if (!refs.Contains(this, value))
                    {
                        refs.Add(this, value);
                    }
                }
            }
        }
    }

    public class Y
    {
        private References refs;

        public Y(References refs)
        {
            this.refs = refs;
        }

        public X X
        {
            get { return refs.Get(this); }
            set
            {
                if (value == null)
                {
                    refs.Delete(this);
                }
                else
                {
                    if (!refs.Contains(value, this))
                    {
                        refs.Add(value, this);
                    }
                }
            }
        }
    }