Maintain one to one mapping between objects
i have the following two classes that provide a one to one mapping between each other. How do i handle null values, when i run the second test i get a stackoverflow exception. How can i stop this recursive cycle? Thanks
[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.IsNu开发者_运维问答ll(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;
}
}
}
}
}
That works fine:
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;
}
}
}
}
}
When setting y.X = null;
, what happens is that it will try to set y.X to null as _x is not null, which in turn tries to set (y.X).Y to null since _y in x is still not null yet and ... well you get the idea - an endless loop.
I've changed it so that the member value is assigned first before assigning to the property of the member variable.
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;
}
}
}
}
}
You're running into infinite loop here.
If you just don't want to get null
s, use this:
get
{
if (_y == null)
_y = new Y();
return _y;
}
Use separate entity to store relationships between objects. Like this:
[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);
}
}
}
}
}
精彩评论