2013-03-14 1 views
2

J'essaie de comparer deux objets complexes en C#, et de produire un dictionnaire contenant les différences entre les deux.Réflexion - comparaison d'objets et valeurs par défaut

Si j'ai une classe comme ceci:

public class Product 
{ 
    public int Id {get; set;} 
    public bool IsWhatever {get; set;} 
    public string Something {get; set;} 
    public int SomeOtherId {get; set;} 
} 

Et un exemple, ainsi:

var p = new Product 
        { 
         Id = 1, 
         IsWhatever = false, 
         Something = "Pony", 
         SomeOtherId = 5 
        }; 

et un autre:

var newP = new Product 
    { 
     Id = 1, 
     IsWhatever = true 
    }; 

Pour obtenir les différences entre ceux-ci, i fais des choses qui comprend ceci:

var oldProps = p.GetType().GetProperties(); 
var newProps = newP.GetType().GetProperties(); 

// snip 
foreach(var newInfo in newProps) 
{ 
    var oldVal = oldInfo.GetValue(oldVersion, null); 
    var newVal = newInfo.GetValue(newVersion,null); 
} 

// snip - some ifs & thens & other stuff 

et il est cette ligne qui est d'intérêt

var newVal = newInfo.GetValue(newVersion,null); 

En utilisant l'exemple des objets ci-dessus, cette ligne me donnerait une valeur par défaut de 0 pour SomeOtherId (même histoire pour bools & DateTimes & whathaveyou).

Qu'est-ce que je cherche est d'avoir newProps inclure uniquement les propriétés qui sont explicitement spécifiées dans l'objet, donc dans l'exemple ci-dessus, Id et . J'ai joué environ avec BindingFlags en vain.

Est-ce possible? Y a-t-il une façon plus propre/meilleure de le faire, ou un outil qui est là pour me sauver la peine?

Merci.

+0

try [AutoMapper] (http: // automapper.org /) (gratuit) ... – Yahia

+2

Il n'existe aucun moyen de savoir si une propriété a été explicitement initialisée de cette manière. Le CLR n'en garde tout simplement pas une trace. – MattW

+0

Pour autant que j'ai compris votre tâche, vous ne pouvez pas faire cela. Vous ne pouvez pas différencier à l'exécution si vous avez créé l'objet en définissant 'bool' sur false ou s'il était défini par défaut lors de la création de l'objet –

Répondre

0

J'ai fini par corriger le problème sans utiliser de réflexion (ou, ne l'utilisant pas au moins de cette façon).

Il va, plus ou moins, comme ceci:

public class Comparable 
{ 
    private IDictionary<string, object> _cache; 

    public Comparable() 
    { 
     _cache = new Dictionary<string, object>(); 
    } 

    public IDictionary<string, object> Cache { get { return _cache; } } 

    protected void Add(string name, object val) 
    { 
     _cache.Add(name, val); 
    } 
} 

Et la mise en œuvre du produit va à ceci:

public class Product : Comparable 
{ 
    private int _id; 
    private bool _isWhatever; 
    private string _something; 
    private int _someOtherId; 

    public int Id {get { return _id; } set{ _id = value; Add("Id", value); } } 
    public bool IsWhatever { get { return _isWhatever; } set{ _isWhatever = value; Add("IsWhatever ", value); } } 
    public string Something {get { return _something; } set{ _something = value; Add("Something ", value); } } 
    public int SomeOtherId {get { return _someOtherId; } set{ _someOtherId = value; Add("SomeOtherId", value); } } 
} 

Et la comparaison est alors assez simple

var dic = new Dictionary<string, object>(); 

foreach(var obj in version1.Cache) 
{ 
    foreach(var newObj in version2.Cache) 
    { 
     //snip -- do stuff to check equality 
     dic.Add(....); 
    } 
} 

Ne salit pas énormément le modèle, et fonctionne bien.

2

Il n'y a pas d'indicateur à indiquer si une propriété a été explicitement définie. Ce que vous pouvez faire est de déclarer vos propriétés en tant que types nullable et comparer la valeur à null.

+0

J'aurais dû mentionner que j'ai pensé à cela, mais je préfère ne pas salir les objets en question avec des nullables. Ce pourrait être ma seule option cependant. Merci pour les commentaires en tout cas. –

+0

Eh bien, un peu similaire à cela, mais un peu plus laid serait de laisser votre type comme nullable et comparer les valeurs à 'default (T)', mais dans ce cas, 'IsWhatever = false' ne serait pas modifié. –

1

Si je vous comprends bien, c'est ce que Microsoft a fait avec les classes d'emballage xml, générés avec l'utilitaire xsd, où vous avez eu un XIsSpecified, ou quelque chose comme ça, pour chaque propriété X.

Donc, c'est ce que vous pouvez faire aussi bien - au lieu de public int ID{get;set;}, ajouter un membre privé _id, ou tout ce que vous choisissez de l'appeler, et une propriété booléenne IDSpecified qui sera défini sur true chaque fois poseur de Id est appelé

+0

Intéressant - laissera les déclarations de classe un peu encombré mais pourrait être la meilleure option. Merci pour la suggestion. –