使用相同的接口

时间:2015-11-16 15:33:10

标签: c# interface

我有一个方法可以比较共享相同接口的2个类的属性。 涉及到许多属性而不是检查每个属性,我宁愿迭代它们,从而节省了重复编码。 我认为用反射做这个可能是可能的,但我无法弄清楚如何。 这段代码失败了,因为我不明白我在做什么。

    public void IsEqual(IPropertyComparer src, IPropertyComparer property)
    {
        PropertyInfo[] srcPI = src.GetType().GetPublicProperties();
        PropertyInfo[] propertyPI = property.GetType().GetPublicProperties();
        var numberOfProperties = srcPI.Count();
        for (var i = 0; i < numberOfProperties; i++)
        {
            var srcValue = srcPI[i].GetValue(src, null);
            var propertyValue = propertyPI[i].GetValue(property, null);
            var propertyName = srcPI[i].Name;
            if(srcValue.Equals(propertyValue) == false)
            {
                this.Action = "ValidateExistingPropertyFailOn" + propertyName;
                this.Data.Error = true;
                this.Data.ErrorMessage =
                    string.Format(this.Template, this.UPRN, propertyName, srcValue, propertyValue);
                return;
            }
        }

代码中有一个扩展方法。扩展方法失败,因为Type未被识别为接口;

public static PropertyInfo[] GetPublicProperties(this Type type)
{
    if (type.IsInterface)
    {
        var propertyInfos = new List<PropertyInfo>();

        var considered = new List<Type>();
        var queue = new Queue<Type>();
        considered.Add(type);
        queue.Enqueue(type);
        while (queue.Count > 0)
        {
            var subType = queue.Dequeue();
            foreach (var subInterface in subType.GetInterfaces())
            {
                if (considered.Contains(subInterface))
                {
                    continue;
                }
                considered.Add(subInterface);
                queue.Enqueue(subInterface);
            }

            var typeProperties = subType.GetProperties(
                BindingFlags.FlattenHierarchy
                | BindingFlags.Public
                | BindingFlags.Instance);

            var newPropertyInfos = typeProperties
                .Where(x => !propertyInfos.Contains(x));

            propertyInfos.InsertRange(0, newPropertyInfos);
        }

        return propertyInfos.ToArray();
    }

    return type.GetProperties(BindingFlags.FlattenHierarchy
        | BindingFlags.Public | BindingFlags.Instance);
}

3 个答案:

答案 0 :(得分:1)

如果我理解正确,你会尝试比较两个共同实现接口的对象,并且只比较接口中定义的属性进行比较。

如果是这种情况,请尝试:

<强> EDITED

为防止出现空异常,我编辑了代码:

bool IsEqual(IPropertyComparer o1, IPropertyComparer o2)
{
    var props = typeof(IPropertyComparer).GetProperties();

    foreach(var prop in props)
    {
        var v1 = prop.GetValue(o1);
        var v2 = prop.GetValue(o2);

        if(v1 == null)
        {
            if(v2 != null) return false;
        }
        else
        {
            if(!v1.Equals(v2)) return false;
        }
    }

    return true;
}

答案 1 :(得分:1)

如果您对比较两个对象实例的需求变得更加复杂,那么您应该在NuGet上查看这个包:

https://www.nuget.org/packages/CompareNETObjects/

和codeplex页面:

http://comparenetobjects.codeplex.com/

非常擅长给你一个&#34;差异&#34;,有时可能是你真正想要的......

更新 - 基于对象内的字段检查Equality的通用,可重用的方法。

我偶然发现了吉米·博加德写的ValueObject<T>课程,这可能引起一些人的兴趣。它的最初目的非常适合于DDD,其中相同属性的对象被认为是相同的,无论它们的基于引用类型的哈希码如何。

http://grabbagoft.blogspot.com/2007/06/generic-value-object-equality.html

这是一个稍微更新的版本,使用Linq来提高可读性和处理前置条件检查的更好方法 - 例如x.ForNull(nameof(firstInCompare));

public abstract class ValueObject<T> : IEquatable<T> where T : ValueObject<T>
{
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        var other = obj as T;

        return Equals(other);
    }

    public override int GetHashCode()
    {
        var fields = GetFields();

        const int startValue = 17;
        const int multiplier = 59;

        return fields
            .Select(field => field.GetValue(this))
            .Where(value => value != null)
            .Aggregate(startValue, (current, value) => current * multiplier + value.GetHashCode());
    }

    public virtual bool Equals(T other)
    {
        if (other == null)
            return false;

        var t = GetType();
        var otherType = other.GetType();

        if (t != otherType)
            return false;

        var fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        foreach (var field in fields)
        {
            var value1 = field.GetValue(other);
            var value2 = field.GetValue(this);

            if (value1 == null)
            {
                if (value2 != null)
                    return false;
            }
            else if (!value1.Equals(value2))
                return false;
        }

        return true;
    }

    private IEnumerable<FieldInfo> GetFields()
    {
        var t = GetType();

        t.ForNull("this");

        var fields = new List<FieldInfo>();

        while (t != typeof(object))
        {
            if (t == null) continue;
            fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));

            t = t.BaseType;
        }

        return fields;
    }

    public static bool operator ==(ValueObject<T> firstInCompare, ValueObject<T> secondInCompare)
    {
        firstInCompare.ForNull(nameof(firstInCompare));
        secondInCompare.ForNull(nameof(secondInCompare));

        return firstInCompare?.Equals(secondInCompare) ?? false;
    }

    public static bool operator !=(ValueObject<T> firstInCompare, ValueObject<T> secondInCompare)
    {
        return !(firstInCompare == secondInCompare);
    }
}

预处理防护可以在像这样的静态助手类中找到

public static class Guard
{
    public static void ForLessEqualZero(this int value, string parameterName)
    {
        if (value <= 0)
        {
            throw new ArgumentOutOfRangeException(parameterName);
        }
    }

    public static void ForPrecedesDate(this DateTime value, DateTime dateToPrecede, string parameterName)
    {
        if (value >= dateToPrecede)
        {
            throw new ArgumentOutOfRangeException(parameterName);
        }
    }

    public static void ForNullOrEmpty(this string value, string parameterName)
    {
        if (string.IsNullOrEmpty(value))
        {
            throw new ArgumentOutOfRangeException(parameterName);
        }
    }

    public static void ForNull<T>(this T value, string parameterName)
    {
        ForValueType<T>(parameterName);

        if (value == null)
        {
            throw new ArgumentNullException(parameterName);
        }
    }

    private static void ForValueType<T>(string parameterName)
    {
        if (typeof(T).IsValueType)
        {
            throw new ArgumentException("parameter should be reference type, not value type", parameterName);
        }
    }
}

要以通用,可重复使用的方式检查相等性,可以将对象包装在ValueObject<T>中。

或者您可以窃取实施并以更具体的方式执行。

答案 2 :(得分:0)

我前段时间一直在找这样的东西,最后设法编写了这个功能。它也适用于您的界面。

bool IsEqual(object obj)
    {
        var type = this.GetType();
        bool SameObj = true;
        //for each public property from your class
        type.GetProperties().ToList().ForEach(prop =>
        {
            //dynamically checks there equals
            if (!prop.GetValue(this, null).Equals(prop.GetValue(obj, null)))
            {
                SameObj = false;
            }
        });
        return SameObj;
    }