2017-08-25 71 views
0

我有2个包含另一个类属性的类。从堆中获取先前实例化的对象以获取变量引用

public class Client 
{ 
    public Customer Customer { get; set; } 

    public Client() 
    { 

    } 
} 

public class Customer 
{ 
    public Client Client { get; set; } 

    public Customer(int id) 
    { 
     // some retrieval logic by using id ... 
    } 
} 

这是一个工厂类,显示了我想要在编译时间之前知道对象的想法。实际上,我不需要通过用户提供的数据进行反射来实例化它们。现在,考虑到这一点,我找对象的属性分配的相反类以前创建的对象,其他类(即像下图)

public class Factory 
{ 
    public Factory() 
    { 
     // this is all done via reflection at run-time in my real code 
     Client client = new Client(); 
     client.Customer = new Customer(1); // id would be retrieve from a client property (not shown for simplicity sake) 
     client.Customer.Client = client; // refer to the first object of client 
    } 
} 

然而,利用先前提供的工厂类例如,我的代码中的客户端对象不在作用域中,或者在堆栈中更高(各种属性都以递归方式实例化,并且如果较高类在子类/对象中具有相同类型的属性,则它将被跳过(以避免无限递归)

如何在工厂方法中利用类名称或任何对象的属性将属性指向先前实例化的对象(可能不是)在适用范围?

我正在考虑使用列表/字典来存储更高级别的对象引用,并通过递归将它们传递下来,并检查对象的类型是否匹配子后续属性的类型,然后只使用存储在该属性的列表/字典。我想看看这是最好的方式还是另一种方式。

这是纯粹的理论,可能会或可能不会被使用。目标是任何时间和对象都是从这些类创建的,所有相关的类在第一个对象中都包含属性,这些属性包含从不为空/不会无限回避的相关类的实例化实例。

+1

答案在问题中。 – Guillaume

+0

@Guillaume谢谢,我知道,但我只是想知道这是最好的方式,还是有更好的方法。 – B1313

回答

0

我建议你使用工厂的CustomerClient,只有一个小的增强:你会想使用实现缓存的工厂。可以找到一个例子in this answer

当您构建一个Customer时,只需从高速缓存工厂填充Client即可。反之亦然。缓存机制将负责其余部分。当然,你会希望将工厂作为单例实例注入,以便缓存在使用工厂的所有代码中都是公共的。

客户工厂看起来有点像这样:

public class CustomerFactory : ICustomerFactory 
{ 
    private readonly IClientFactory clientFactory; //To be injected 

    private readonly ConcurrentDictionary<int, ICustomer> customers = 
       new ConcurrentDictionary<int, ICustomer>(); 

    public CustomerFactory(IClientFactory clientFactory) 
    { 
     this.clientFactory = clientFactory; //Injected 
    } 

    public ICustomer GetCustomer(int id) 
    { 
     ICustomer customer = this.customers.GetOrAdd(id,() => new Customer(id)); 
     if (customer.Client == null) 
     { 
      customer.Client = this.clientFactory.GetClient(customer.ClientID); 
     } 
     return customer; 
    } 
} 

在上面的例子中,你会发现,新实例化的客户是之前添加到缓存尝试设置客户端属性。这对于避免无限循环非常重要。如果您在客户进入缓存之前尝试检索客户端,ClientFactory将无法找到它,并最终可能会创建一个新实例。

另一方面,也许你不需要立即设置客户端属性。毕竟,你现在有一个缓存机制,所以你可以负担得起懒惰地设置。我们可以从GetCustomer删除代码...

public ICustomer GetCustomer(int id) 
    { 
     return this.customers.GetOrAdd(id,() => new Customer(id)); 
    } 

...并仅在需要时检索客户端。

class Customer 
{ 
    public Client Client 
    { 
     get 
     { 
      return this.clientFactory.GetClient(this.ClientID); 
     } 
    } 
} 

虽然看起来昂贵一遍又一遍地拨打ClientFactory,所发生的一切是在字典中快速查找。如果呼叫者甚至不需要客户端,我们就可以保存到数据库的往返行程。

除了不需要传递任何引用,这整个想法与您对问题结尾提出的想法没有多大区别。唯一需要的参考是对工厂的参考。

+0

然而,我喜欢你的想法(这是因为我没有在我的原始问题中引用它),我的工厂设置方式是通过一个通用工厂(即工厂),并且该工厂的一个实现初始化一个类+子类。泛型工厂“反映”属性并检查特定接口(类所基于的接口),然后通过反射属性的类型作为泛型创建泛型工厂的实例。所以,所有工厂的实例化都是通过动态创建通用工厂来实现的。 – B1313

+0

你让我好奇。你如何反映一个属性的类型,然后把它作为泛型?菱形运算符不接受运行时变量,即。你不能去'var o = new GenericType ' –

+0

.NET反射框架是关键。你可以从你要实例化的泛型类的类型中使用MakeGenericType方法,然后使用GetMethod作为方法,然后使用Activator.CreateInstance(传递MakeGenericType类型结果,构造函数参数[如果有的话])。它对我来说非常适用,但是对于激活器有优化,但我没有彻底调查过它们。它不是在编译时完成的,但我不介意通过在单个方法/类中牺牲少量编译时(AKA Intellisense)知识来实现​​更简洁,更精简和更健壮的代码。 – B1313