避免类的循环引用
本文关键字:循环 引用 | 更新日期: 2023-09-27 18:13:23
我有一个创建并返回一个"Person"的函数。"人"拥有"配偶"财产,当然,这是另一个"人"。这将导致无限递归,因为每次调用函数时创建的"Person"总是一个"新"对象。是否有一种方法可以使用相同的函数(如下所示)而不会导致无限循环?
public PersonModel Load(int personID)
{
PersonModel person = new PersonModel();
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = Helpers.ConnectionDB;
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "LoadPerson";
command.Parameters.Add(new SqlParameter("@PersonID", personID));
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
person.PersonID = int.Parse(reader["PersonID"].ToString());
person.FirstName = reader["FirstName"].ToString();
person.LastName = reader["LastName"].ToString();
person.MiddleName = reader["MiddleName"].ToString();
person.Age = reader["Age"] != DBNull.Value ? int.Parse(reader["Age"].ToString()) : (int?)null;
person.SpouseID = reader["SpouseID"] != DBNull.Value ? int.Parse(reader["SpouseID"].ToString()) : (int?)null;
if (person.SpouseID != null && person.Spouse == null)
{
person.Spouse = this.Load(person.SpouseID.Value);
}
}
conn.Close();
}
return person;
}
使用Dictionary<Int32,PersonModel>
来跟踪加载的实体:
public PersonModel Load(Dictionary<Int32,PersonModel> dict, int personId) {
PersonModel ret;
if( dict.TryGetValue( personId, out ret ) ) return ret;
// load from database here, but do not call recursively just yet
ret = new PersonModel() { ... };
dict.Add( ret.PersonId, ret );
ret.Spouse = this.Load( dict, person.SpouseId.Value );
}
我可以想到几个不同的选项:
-
向
Load()
添加一个可选参数,表示不应该尝试加载配偶。这是一个hack,但非常简单。public PersonModel Load(int personID, bool loadSpouse = true) { ... if (loadSpouse && person.SpouseID != null && person.Spouse == null) { person.Spouse = this.Load(person.SpouseID.Value, false); } ... return person; }
-
使用左外连接选择存储过程中配偶的属性。这将执行得更好,如果您想要选择所有员工,这将更有效,因为它将只是一次数据库访问。您可以在if语句中添加如下代码来构建配偶对象:
person.Spouse = new PersonModel() person.Spouse.FirstName = reader["SpouseFirstName"].ToString(); ...
不完全符合OPs的问题,但既然我是因为标题而来到这里,那么添加一个一般的答案可能会有所帮助。
所以在我的场景中,我需要改变整个图中的一个属性设置它的所有子属性为任意深度
为了确保实例不会执行两次(从而允许StackOverflowException
),我在每次调用中传递HashSet<T>
,因此我们知道要跳过执行的项:
//The executed parameter needn't ever be provided externally.
static void SetState(IObject obj, State state, HashSet<IObject> executed = null)
{
if(executed == null) executed = new HashSet<IObject>(/* provide comparer if needed*/);
if(!executed.Add(obj)) return;
//safe to continue walking graph
obj.State = state;
var props = obj.GetObjectPropertyValues().OfType<IObject>();
foreach (var prop in props)
SetState(obj, state, executed);
}
注意,这些项必须实现适当的相等性和GetHashCode
函数,或者应该将比较参数传递给HashSet
的构造函数。