2011-05-02 7 views
1

j'ai une bibliothèque de classe pour tenir mes objets si:Comment résoudre ce problème "référence circulaire" C#

xxxCommon \ Objects \ Customer.cs

public class Customer 
    { 
     public string url { get; set; } 
     public List<Telephone> telephones { get; set; } 
    } 

xxxData \ DC \ CustomerDC.cs (DataComponent)

  • Cet appel de classe et de nombreux procs retour des objets dans xxxCommon \ objets

Mon Le principal problème est maintenant la référence circulaire, pour faire une charge "paresseux" je dois définir le obtenir des téléphones atributes à une fonction dans xxxData \ DC, comment éviter cela?

Répondre

2

Une façon vous pouvez résoudre une dépendance circulaire est d'avoir une couche entre vos deux ensembles:

Au lieu de ce scénario;

Assemblée Modèle:

public class Customer{ 
    //... 
} 

Assemblée des données:

public class CustomerDAO{ 
    public Customer LoadCustomer(int id){ 
     return new Customer(id,...); 
    } 
} 

où l'ensemble de modèle fait référence à l'ensemble des données et des données ne peut pas atteindre de nouveau dans le modèle pour instancier un client.

Vous pouvez avoir à la place;

Assemblée Modèle:

public class CustomerModel:Customer{} 
public class ModelFactoryImp:ModelFactory{ 
    public Customer CreateCustomer(int id,//...customer params){ 
     return new CustomerModel(...); 
    } 
} 

ModelInterfaces Assemblée:

public abstract class Customer{//...} 
public abstract ModelFactory{ 
    Customer CreateCustomer(int id,//...customer params); 
} 

données Assemblée:

public class CustomerDAO{ 
    private ModelFactory _modelFactory; 

    public CustomerDAO(ModelFactory modelFactory){ 
     _modelFactory = modelFactory; 
    } 

    public Customer LoadCustomer(int id) 
    { 
     // Data Access Code 
     return _modelFactory.CreateCustomer(id,//...cutomer params); 
    } 
} 

Lorsque les deux modèles et ensembles de données dépendent de la couche de ModelInterfaces et vous passez le client Data Access Object une implémentation de la classe ModelFactory afin que je t peut créer des clients.

0

Cela ressemble à une utilisation appropriée pour WeakReferences - vous ne voulez pas tenir toute la liste des clients/téléphones en cache à tout moment, correct? Le API documentation utilise actuellement la gestion d'un cache important à titre d'exemple.

3

Vous pouvez contourner les références circulaires en utilisant des méthodes de rappel. Par exemple, la classe ActiveRecordSQL <T> a une méthode par défaut pour créer des entités, mais permet de l'écraser.

private Func<T> GetNewEntity; 

protected ActiveRecordSQL() // constructor 
{ 
    GetNewEntity = DefaultGetNewEntity; 
} 

protected Result<T> GetWithCustomEntity(SqlCommand cmd, Func<T> GetCustomEntity) 
{ 
    GetNewEntity = GetCustomEntity; 
    return Get(cmd); 
} 

private T DefaultGetNewEntity() 
{ 
    return new T(); 
} 

protected T Populate(DataRow row, T existing) 
{ 
    T entity = existing ?? GetNewEntity(); 
    entity.Populate(row); 
    return entity; 
} 

Une classe qui a besoin de passer une fonction personnalisée peut utiliser une expression lambda, ou passer une référence à sa propre fonction avec la signature à droite.

 ReferrerInfo = dc.Referrers.GetCustomReferrer(referrerID,() => new ReferrerFacade()).Data as ReferrerFacade; 

"GetCustomReferrer" appelle des méthodes intermédiaires qui passent simplement la méthode à "GetWithCustomEntity"."ReferrerFacade" sous-classe une entité et vit dans un projet différent. Passer la méthode de rappel permet aux appels "en arrière" à travers la référence existante.

+0

Ceci est un bon moyen si vous êtes confronté à la tâche difficile de résoudre la dépendance circulaire, mais une bonne conception ne devrait pas éliminer une telle dépendance en premier lieu. –

+1

Toutes les conceptions sont des compromis, et dans tout système complexe, vous trouverez des zones où vous avez besoin d'une communication bidirectionnelle entre les composants. La programmation fonctionnelle offre un certain nombre d'outils, tels que les rappels, les continuations et les fermetures. Une autre solution polyvalente et commune pour le problème est les gestionnaires d'événements; la couche inférieure fournit le gestionnaire d'événements et déclenche des événements, tandis que la couche supérieure enregistre un gestionnaire d'événements pour recevoir des notifications. (Regardez INotifyPropertyChanged dans .NET pour un excellent exemple.) L'injection de dépendance offre une autre approche pour résoudre les problèmes de dépendance. –