简体   繁体   中英

Class properties of certain generic type cast into generic type to execute methods

First I would like to apologize for the messy title. I'm not quite sure how to put it into words so I will describe the situation.

I'm writing a comparison engine for our product, that is capable of comparing different products like this:

public abstract class ComparableProduct
{
    public ComparableProperty<decimal> Weight { get; set; }
    public ComparableProperty<decimal> Width { get; set; }
    public ComparableProperty<decimal> Height { get; set; }
    public ComparableProperty<decimal> Depth { get; set; }
    public bool IsBetterThan(ComparableProduct target){}
}

Actual products are derived from ComparableProduct, such as Screen : ComparableProduct, which adds property

ComparableProperty<decimal> Dimension { get; set; }

This means that I can have a class Laptop which has a property Keyboard, Screen, StorageDevice... etc etc, which are all derived from ComparableProduct.

Those have in turn comparable properties like this:

public abstract class ComparableProperty<T> where T : IComparable<T>
{
    T Value { get; set; }
    public ComparisonType ComparisonType { get; set; }
    public bool IsBetterThan(T target)
    {
        if(ComparisonType == ComparisonType.GreaterThan)
            return Value.CompareTo(target) >= 0;
        return Value.CompareTo(target) <= 0;
    }

    public bool IsBetterThan(IEnumerable<T> targets)
    {
        foreach(var target in targets)
        {
            if (ComparisonType == ComparisonType.SmallerThan && Value.CompareTo(target) >= 0)
                return false;
            if (ComparisonType == ComparisonType.GreaterThan && Value.CompareTo(target) <= 0)
                return false;
        }
        return true;
    }

}

I haven't tested these, by all logic they should work. The trouble I'm having... is with the IsBetterThan method in ComparableProduct. The expected functionality is that on the top class (say, Laptop), is looped through its ComparableProduct properties and called IsBetterThan for the other copy and those will loop through their subproperties... In addition, all the ComparableProduct ComparableProperty properties are checked with IsBetterThan with the other class' equivalent value.

Anyway, here's what I have, and you can see the problem I have immediately.

public bool IsBetterThan(ComparableProduct target)
    {
        foreach(var property in GetType().GetProperties().Where(x => x.PropertyType == typeof(ComparableProduct)))
        {
            var compareTo = target.GetType().GetProperty(property.Name).GetValue(target, null) as ComparableProduct;
            var local = property.GetValue(this, null) as ComparableProduct;
            if(local != null && !local.IsBetterThan(compareTo))
                return false;
        }
        foreach(var property in GetType().GetProperties().Where(x => x.PropertyType == typeof(ComparableProperty<>)))
        {
            var compareTo = target.GetType().GetProperty(property.Name).GetValue(target, null) as ComparableProperty<>;
            var local = property.GetValue(this, null) as ComparableProperty<>;
            if(local != null && !local.IsBetterThan(compareTo))
                return false;
        }
    }

As you can see, I'm trying to cast it to ComparableProperty<>, which means it's missing the generic type. However, I'm not quite sure how to get the generic type of the involved property.

Also, if there's a better way of doing it... I will take any tips I can, but this is the first half decent way of doing it that came to my mind.

EDIT:

Spoke too soon. When I try to enumerate the properties in ComparableProduct's IsBetterThan like this:

foreach(var property in GetType().GetProperties())
        {
            var t = property.GetType().GetInterfaces();
            if (!property.GetType().GetInterfaces().Contains(typeof(IComparableProperty))) continue;

            var compareTo = target.GetType().GetProperty(property.Name).GetValue(target, null) as IComparableProperty;
            var local = property.GetValue(this, null) as IComparableProperty;
            if (local == null) continue;
            if(!local.IsBetterThan(compareTo))
                return false;
        }

Then it appears it can't find IComparableProperty in the interfaces. I have gone through the main methods that may contain it... but the only interfaces t contains are ICustomAttributeProvider, _MemberInfo, _PropertyInfo and ISerializable.

EDIT 2:

I have solved this by falling back on string comparison on

if (property.PropertyType.Name != "ComparableProperty`1") continue;

And after changing T to ComparableProperty and IEnumerable to IEnumerable> the whole comparison works perfectly.

You can create a non-generic interface, and then work with it:

 public interface IComparableProperty
    {
        bool IsBetterThan(object target);

        bool IsBetterThan(IEnumerable targets);
    }

    public abstract class ComparableProperty<T>: IComparableProperty where T : IComparable<T>
    {
        T Value { get; set; }
        public ComparisonType ComparisonType { get; set; }
        public bool IsBetterThan(T target)
        {
            if (ComparisonType == ComparisonType.GreaterThan)
                return Value.CompareTo(target) >= 0;
            return Value.CompareTo(target) <= 0;
        }

        public bool IsBetterThan(IEnumerable<T> targets)
        {
            foreach (var target in targets)
            {
                if (ComparisonType == ComparisonType.SmallerThan && Value.CompareTo(target) >= 0)
                    return false;
                if (ComparisonType == ComparisonType.GreaterThan && Value.CompareTo(target) <= 0)
                    return false;
            }
            return true;
        }

        bool IComparableProperty.IsBetterThan(object target)
        {
            return IsBetterThan((T) target);
        }

        bool IComparableProperty.IsBetterThan(IEnumerable targets)
        {
            return IsBetterThan((IEnumerable<T>) (targets));
        }
    }

EDIT 0:

Did you try to use this method Type.IsAssignableFrom(Type) like this:

foreach (var property in GetType().GetProperties())
        {
            if (!typeof(IComparableProperty).IsAssignableFrom(property.PropertyType)) continue;

            var compareTo = target.GetType().GetProperty(property.Name).GetValue(target, null) as IComparableProperty;
            var local = property.GetValue(this, null) as IComparableProperty;
            if (local == null) continue;
            return local.IsBetterThan(compareTo);
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM