Pergunta

I'm trying to compare two complex objects in C#, and produce a Dictionary containing the differences between the two.

If I have a class like so:

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

And one instance, thus:

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

and another:

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

To get the differences between these, i'm doing stuff that includes this:

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

and it's this line that's of interest

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

Using the example objects above, this line would give me a default value of 0 for SomeOtherId (same story for bools & DateTimes & whathaveyou).

What i'm looking for is a way to have newProps include only the properties that are explicitly specified in the object, so in the above example, Id and IsWhatever. I've played about with BindingFlags to no avail.

Is this possible? Is there a cleaner/better way to do it, or a tool that's out there to save me the trouble?

Thanks.

Foi útil?

Solução 3

I ended up fixing the issue without using reflection (or, not using it in this way at least).

It goes, more or less, like this:

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);
    }
}

And the product implementation goes to this:

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); } }
}

And the comparison is then pretty straightforward

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(....);
    }
}

Doesn't hugely dirty the model, and works nicely.

Outras dicas

There is no flag to tell if you a property was explicitly set. What you could do is declare your properties as nullable types and compare value to null.

If i understand you correctly, this is what microsoft did with the xml wrapping classes, generated with the xsd utility, where you had a XIsSpecified, or something like that, for each property X.

So this is what You can do as well - instead of public int ID{get;set;}, add a private member _id , or whatever you choose to call it, and a boolean property IDSpecified which will be set to true whenever Id's setter is called

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top