【问题标题】:Why are my types not marked as equal?为什么我的类型没有被标记为相等?
【发布时间】:2015-05-10 03:25:43
【问题描述】:

在 SO 上还有其他一些与此相关的问题,但我觉得他们都没有真正提供可靠的答案。

我最近经常使用反射,我想检查一些实现某个接口的程序集中包含的类型。

所以我有一个名为 BESCollector 的类,它实现了 ICollector

public class BESCollector : ICollector
{
  // ...
}

在这里我加载程序集,循环遍历所有类型并查看该类型是否包含 ICollector 类型的接口...

Assembly pluginAssembly = Assembly.ReflectionOnlyLoadFrom(pluginConfig.AssemblyLocation);
IEnumerable<Type> types = pluginAssembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ICollector)));

...没有产生任何结果:(。在调试时我可以清楚地看到它确实包含这种类型。我在 if-loop 上中断

Assembly pluginAssembly = Assembly.ReflectionOnlyLoadFrom(pluginConfig.AssemblyLocation);
IEnumerable<Type> types = pluginAssembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(ICollector)));
foreach (Type type in pluginAssembly.GetTypes())
{
    Type[] interfaces = type.GetInterfaces();
    if (interfaces.Contains(typeof(ICollector)))
    {
        Console.WriteLine(@"\o/");
    }
}

这些结果直接来自调试器。在这里可以看到interfaces包含一个Type,即IColector:

-       interfaces  {System.Type[1]}    System.Type[]
  +     [0] {Name = "ICollector" FullName = "SquidReports.DataCollector.Interface.ICollector"}  System.Type {System.ReflectionOnlyType}

而我调用 .GetInterfaces() 的类型显然是 BESCollector:

+       type    {Name = "BESCollector" FullName = "SquidReports.DataCollector.Plugin.BES.BESCollector"} System.Type {System.ReflectionOnlyType}

但是等式声明 interfaces.Contains(typeof(ICollector)) 永远不会计算为真。

只是为了让您认为我没有在这里混淆类型,当我在调试时将鼠标悬停在 (typeof(ICollector)) 上时,它会清楚地显示 SquidReports.DataCollector.Interface.ICollector

当然,这确实有效:

Type[] interfaces = type.GetInterfaces();
if (interfaces.Any(t => t.Name == typeof(ICollector).Name))
{
    Console.WriteLine(@"\o/");
}

但这并不能告诉我很多,除了同名的类型。

另外,为什么这个检查会失败?

if (typeof(ICollector).Equals(typeof(ICollector)))
{
    Console.WriteLine("EQUALIZED");
}

为什么我的第一次相等检查失败了?更具体地说,类型相等在 C# 5.0 中是如何工作的? 'Type' 类型的 .Equals() 没有具体实现吗?

编辑:

在下面的 I Quality Catalyst 的要求下,如果接口和类在同一个程序集中定义,则快速测试是否相等性评估为真。这确实有效:

class Program
{
    public interface ITest
    {

    }

    public class Test : ITest
    {
        public int ID { get; set; }
    }

    static void Main(string[] args)
    {
        Type[] interfaces = (typeof(Test)).GetInterfaces();
        if (interfaces.Any(t => t == typeof(ITest)))
        {
            Console.WriteLine(@"\o/");
        }
    }
}

EDIT2:这是 ICollector 的实现

public interface ICollector
{
    IDbRelay DbRelay { get; set; }
    ILogManager LogManager { get; set; }

    void Init(ILogManager logManager, IDbRelay dbRelay);
    void Execute();
}

但是,我相信我可能错过了一个重要的细节。我在这里使用三个程序集:

  1. 我正在执行相等性检查的“主要”(SquidReports.DataCollector) 程序集
  2. 包含 ICollector 的“接口”(SquidReports.DataCollector.Interface) 程序集
  3. 包含 BESCollector 定义的“插件”(SquidReports.DataCollector.Plugin.BES) 程序集

请注意,“接口”(SquidReports.DataCollector.Interface) 是从 ReflectionOnlyAssemblyResolve 事件中加载的,如下所示,因为 ReflectionOnlyLoadFrom() 不会自动解析依赖项,这可能非常重要:

public Assembly ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
{
    // This event is used to resolve dependency problems. We need to return the requested assembly in this method.
    // We should probably look in two locations:
    //   1. The SquidReports.DataCollector folder
    //   2. The Corresponding folder in SquidReports.DataCollector\Plugins    

    // Let's first turn the 'full' assembly name into something more compact
    AssemblyName assemblyName = new AssemblyName(args.Name);
    this.Logger.LogMessage(LogLevel.Debug, String.Format("Attempting to resolve Assembly {0} to load {0}", assemblyName.Name, args.RequestingAssembly.GetName().Name));

    // Let's also get the location where the requesting assembly is located
    DirectoryInfo pluginDirectory = Directory.GetParent(args.RequestingAssembly.Location);
    string assemblyFileName = String.Format("{0}.dll", assemblyName.Name);

    if (File.Exists(assemblyFileName))
    {
        // It's in the main bin folder, let's try to load from here
        return Assembly.ReflectionOnlyLoadFrom(assemblyFileName);
    }
    else if (File.Exists(Path.Combine(pluginDirectory.FullName, assemblyFileName)))
    {
        // It's in the plugin folder, let's load from there
        return Assembly.ReflectionOnlyLoadFrom(Path.Combine(pluginDirectory.FullName, assemblyFileName));
    }

    return null;
}

EDIT3:在下面 Joe 的建议下,我再次使用以下代码调试了应用程序:

if (type.Name == "BESCollector")
{
    Type[] interfaces = type.GetInterfaces();
    Type interface1 = interfaces[0];
    Type interface2 = typeof(ICollector);
    Console.WriteLine("");
}

我复制了 interface1interface2 的完整属性,然后将它们放入文件 diff 中。

我认为结果可能解决了我的问题:

interface1描述如下:

interface1  {Name = "ICollector" FullName = "SquidReports.DataCollector.Interface.ICollector"}  System.Type {System.ReflectionOnlyType}

interface2 是这样描述的:

interface2  {Name = "ICollector" FullName = "SquidReports.DataCollector.Interface.ICollector"}  System.Type {System.RuntimeType}

那么我是否正确地假设问题是因为 {System.RuntimeType} != {System.ReflectionOnlyType} 引起的,而 Assembly.ReflectionOnlyLoadFrom() 应该为此负责?

【问题讨论】:

  • 您的相等性检查是否适用于同一程序集中定义的任何其他接口?
  • @Quality Catalyst:实际上这似乎有效,请参阅上面的更新。我不太了解这里。
  • 你能不能也给我们看看`ICollector'的声明?
  • @RobinMattheussen 是与接口在同一个程序集中的 Deriving 类吗?

标签: c# reflection types interface


【解决方案1】:

感谢 Joe Enzminger,我偶然发现了答案:

看来,因为我使用的是Assembly.ReflectionOnlyLoadFrom(),所以我从 Assembly 加载的 Type 实际上是 System.ReflectionOnlyType 而不是 System.RuntimeType。

基本上,有一个简单且非常酷的解决方案:

  1. 停止使用ReflectionOnlyLoadFrom() 并切换到LoadFrom()。这解决了你所有的问题。当然,除非您出于充分的理由使用 ReflectionOnlyLoadFrom。
  2. 在这种情况下,您可以简单地使用 Type.GUID。无论从哪个上下文加载类型,GUID 都是相同的。所以你可以简单地做

    如果 ((typeof(MyReflectionType)).GUID == (typeof(MyType)).GUID) { // 废话... }

这应该可以解决你的问题:)

【讨论】:

  • 比较 GUID 的好主意。
猜你喜欢
  • 2013-04-24
  • 2020-03-18
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 2022-12-19
  • 1970-01-01
相关资源
最近更新 更多