【问题标题】:Check closet inheritance of objects to declared type检查对象的壁橱继承到声明的类型
【发布时间】:2020-07-25 11:43:55
【问题描述】:

在比较类型时,我试图选择正确的方法从容器中选择对象。 在下面的示例中,有 2 个接口:ISomeInterface、ISomeSubInterface,其中 ISomeSubInterface 继承自 ISomeInterface

还有2个类:InterfaceClass、SubInterfaceClass,其中名称表示继承的接口。

很快: InterfaceClassis ISomeInterface

SubInterfaceClass 是 ISomeSubInterface 是 ISomeInterface

在 IsOfType 方法中,我检查匹配的对象。

当前状态: 该方法检查容器中的对象是否属于声明的类型。当调用具有 ISomeInterface 泛型类型的方法时,如果容器中存在这两个类,则它们都匹配,因为它们都“是”ISomeInterface。

通缉状态: 该方法将返回与 ISomeInterface 具有最接近继承关系的对象。

随着容器保存对象,并且正在比较运行时类型,我明白为什么我当前的实现失败了。 我的问题是,是否有可能实施这种返回通缉状态的机制? 我不排除更改数据结构或将对象添加到该数据结构的方式。

class Program
{
    static void Main(string[] args)
    {
        _ = new Test();
    }
}

public class Test
{
    private readonly List<ISomeInterface> _objects = new List<ISomeInterface>();

    public Test()
    {
        ISomeInterface obj1 = new InterfaceClass();
        ISomeInterface obj2 = new SubInterfaceClass();

        _objects.Add(obj1);
        _objects.Add(obj2);

        IEnumerable<ISomeSubInterface> retObjs1 = Get<ISomeSubInterface>();
        int count1 = retObjs1.Count(); // 1 As expected

        IEnumerable<ISomeInterface> retObjs2 = Get<ISomeInterface>();
        int count2 = retObjs2.Count(); // 2, wanted 1
    }

    private IEnumerable<T> Get<T>()
        where T : ISomeInterface
    {
        return _objects.Where(IsOfType<T>).Cast<T>();
    }

    private bool IsOfType<T>(ISomeInterface obj)
        where T : ISomeInterface
    {
        return typeof(T).IsAssignableFrom(obj.GetType());
    }
}

internal class SubInterfaceClass: ISomeSubInterface { }
internal class InterfaceClass: ISomeInterface { }
internal interface ISomeSubInterface : ISomeInterface { }
internal interface ISomeInterface { }

不胜感激这方面的帮助。 谢谢。

【问题讨论】:

  • 您的代码无法编译,顺便说一句。
  • 谢谢,已编辑..
  • 这样更好。这是微不足道的,但很高兴这些东西只是复制、粘贴和运行。 :-)
  • @haim770 - 接近重复,但不完全一致。该问题显示了如何构建一个查询来回答这个问题,但它不是一回事。

标签: c# generics inheritance types


【解决方案1】:

取决于“最近”的定义,这可能不是一项简单的任务。

Tried-N-True

现在,让我们专注于您的示例。与@OguzOzgul 的解决方案相比,我将从不同的角度克服这个问题,但仍然使用GetInterfaces。

GetInterfaces 在你的情况下返回接口类型的顺序与这些类型的继承顺序相同:

SubInterfaceClass
0: ISomeSubInterface
1: ISomeInterface

InterfaceClass
0: ISomeInterface

如您所见,您可以通过查找ISomeInterface 所在的最低可能索引来找到“最接近”的实现类型。在您的情况下,ISomeInterface 位于InterfaceClass 的接口列表中的位置 0,因此您将选择 InterfaceClass。

这可以通过聚合器完成:

private T GetClosetImplementationAsPerReflection<T>()
    where T : ISomeInterface
{
    var result = _objects.Where(IsOfType<T>)
        .Aggregate(
            // Default entry
            new { obj = default(T), rank = int.MaxValue },
            // Aggregator
            (currentEntry, o) =>
            {
                // Find all interface types implemented by the instance type
                // Because your class types do not have base type, the order of interfaces
                // appear in the list is same as how the interfaces are inherited.
                // e.g. 
                // SubInterfaceClass: 0->ISomeSubInterface, 1->ISomeInterface
                // InterfaceClass: 0->ISomeInterface
                var entryFound = o.GetType()
                .GetInterfaces()
                .Select((interfaceType, index) => new { interfaceType, index })
                .First(_ => _.interfaceType == typeof(T));

                // Now you could find the type / instance with ISomeInterface
                // at the lowest possible index
                if (entryFound.index < currentEntry.rank)
                {
                    return new { obj = (T)o, rank = entryFound.index };
                }
                else
                {
                    return currentEntry;
                }
            });


    return result.obj;
}

但是

以上是仅针对您的琐碎案例的经过验证的真正解决方案。实际上,没有这样“最接近”的定义。

编译器将SubInterfaceClass编译成这样:

private class SubInterfaceClass : ISomeSubInterface, ISomeInterface
{
}

所以ISomeInterface 与InterfaceClass 相比,可以说与SubInterfaceClass 相当接近。尽管您可能会争辩说,当ISomeInterface 出现在ISomeSubInterface 之后,那么ISomeSubInterface 更接近,不幸的是,您的代码看不到该顺序。 And it is not guaranteed by GetInterfaces method either.

我的建议是,定义“最接近”对您意味着什么,并专门为您自己的“最接近”实现逻辑。

【讨论】:

    【解决方案2】:

    您可以通过像这样更改 IsOfType&lt;T&gt; 方法来实现此目的。

    这个 linQ 语句可能有更好的实现(可能更有效),但它应该能满足您的需求。

    private bool IsOfType<T>(ISomeInterface obj)
        where T : ISomeInterface
    {
        // The following statement does the following:
        // 1. Check if assignable. If yes;
        // 2. Get all interfaces other than the one we are looking for (T),
        // 3. And see if any of them inherits (T), and if yes, return false.
        // Please see the ! in front of obj.GetType() in the second line
        return
            typeof(T).IsAssignableFrom(obj.GetType()) &&             // Check if assignable first
            !obj.GetType().GetInterfaces()                           // Then, get all interfaces of type
                .Except(new Type[] { typeof(T) })                    // except the interface we are testing against (T)
                .Any((IType) => typeof(T).IsAssignableFrom(IType));  // and see if any of them inherits (T)
    }
    

    【讨论】:

      猜你喜欢
      • 2012-01-10
      • 2011-03-06
      • 2012-10-09
      • 2016-10-14
      • 2012-07-14
      • 1970-01-01
      • 2012-10-01
      • 1970-01-01
      • 2013-10-12
      相关资源
      最近更新 更多