4

With

public abstract class CompositionPlugin { ... }

and

public class MyCompositionPlugin : CompositionPlugin { ... }

I want to check if an object's type is equal to a given type:

public class Framework {

    public IList<CompositionPlugin> CompositionPlugins = new List<CompositionPlugin>();

    public CompositionPlugin GetCompositionPlugin(Type ofType)
    {
        foreach (CompositionPlugin plugin in CompositionPlugins)
        {
            if (plugin.GetType().Equals(ofType))
                return plugin;
        }

        throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
    }
}

Where the method is called like this:

Framework framework = new Framework();
// Adding the plugin to the framework is done by loading an assembly and
// checking if it contains plugin-compatible classes (i.e. subclasses
// of <CompositionPlugin>)
framework.RegisterAssembly("E:\\Projects\\Framework\\MyPlugin\\bin\\Debug\\MyPlugin.dll");
CompositionPlugin plugin = framework.GetCompositionPlugin(typeof(MyCompositionPlugin));

Yet, when testing, this check always fails, even though I most definitely have that type of object in the list that I request.

In my understanding, it should return the first instance of MyCompositionPlugin that is found inside the CompositionPlugins-List.

Is my type check wrong? Why? How is it done correctly?

F.P
  • 17,421
  • 34
  • 123
  • 189
  • Put some more code. How do you generate the ofType parameter? Why the test failed? Did you debug it? – Amiram Korach Aug 01 '12 at 14:20
  • 1
    @tostringtheory it will return the exact type. Could be a class that inherits CompositionPlugins – Amiram Korach Aug 01 '12 at 14:21
  • I added some more code and what I expect as a return value. – F.P Aug 01 '12 at 14:27
  • Your code works for me, perhaps it's a problem with your test? – magritte Aug 01 '12 at 14:34
  • 1
    Perhaps you should add some debugging breakpoints and manually check that the types going in are what you expect. Also, are you trying to load these types from assemblies dynamically at runtime? Sometimes .NET loads the types as independent unique types in different domains even though they're the "same" – Chris Sinclair Aug 01 '12 at 14:35
  • 1
    Weird, I've tried your code verbatim (just adding empty definitions for CompositionPlugin and MyCompositionPlugin : CompositionPlugin) and it works (.net 4 on Windows 7). What version of .net are you using? Is CompositionPlugin defined in a separate assembly? Look at this for instance: http://stackoverflow.com/questions/3623358/two-types-not-equal-that-should-be – Paolo Falabella Aug 01 '12 at 17:18
  • 1
    Oh you are a lifesaver. I didn't want to make my code example too complex, so I left out the part that the plugins are actually loaded from an assembly. Sorry about that. I now use the `AssemblyQualifiedName` for the comparison and it works fine. Maybe you should add an answer with that solution – F.P Aug 02 '12 at 05:52
  • Glad it helped Florian. Feel free to add an answer yourself with how you solved the problem and accept it. I just pointed you to someone else's answer... :-) – Paolo Falabella Aug 02 '12 at 15:55

5 Answers5

9

You want to use IsAssignableFrom on your Type:

if (ofType.IsAssignableFrom(plugin.GetType())

Equals only handles cases where types are exactly the same. IsAssignableFrom also handles the case where ofType may be a type that your plugin inherits from, or an interface that is implemented.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
5

Not an answer but too long for a comment... Are you sure the issue is not in how you call the method or populate your collection? The comparison itself should be ok, as demonstrated by this simplified version of your code:

class A {}

bool TestType(A item, Type ofType)
{       
    return item.GetType().Equals(ofType);
}

now:

Console.WriteLine(TestType(new A(), typeof(A))); // True
Console.WriteLine(TestType(new A(), typeof(string))); // False

EDIT

I think @vcsjones is right. You're trying to compare a derived class to a base class. In the line foreach (CompositionPlugin plugin in CompositionPlugins) you're declaring plugin to be a CompositionPlugin but in the client code you're comparing it with typeof(MyCompositionPlugin). (RE-EDIT no, I'm wrong, your case corresponds to the 4th of my Console.WriteLines that returns true)

See this example with a truth table for Equals in a scenario similar to yours:

class CompositionPlugin {}
class MyCompositionPlugin : CompositionPlugin  {}

// Define other methods and classes here
bool TestType(CompositionPlugin item, Type ofType)
{       
    return item.GetType().Equals(ofType);
}

now

Console.WriteLine(TestType(new CompositionPlugin(), 
            typeof(CompositionPlugin))); //True
Console.WriteLine(TestType(new CompositionPlugin(), 
            typeof(MyCompositionPlugin))); //False
Console.WriteLine(TestType(new MyCompositionPlugin(), 
            typeof(CompositionPlugin))); //False
Console.WriteLine(TestType(new MyCompositionPlugin(),
            typeof(MyCompositionPlugin))); //True
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • I added more of the code around the method, maybe this will clear things up. I, too, think that the comparison should work the way I want it to... – F.P Aug 01 '12 at 14:30
  • @FlorianPeschka looking at your code it seems that you're checking a CompositionPlugin against a MyCompositionPlugin (which is I guess inheriting from CompositionPlugin). I think that's why the equality fails. i.e. vcsjones is right – Paolo Falabella Aug 01 '12 at 14:40
3

use the keyword is

if (plugin is ofType)
    return plugin;

EDIT:

I have to go with @vcsjones on this one. Use the isassignablefrom function.

But if you really think it should work, what I always do is create quick function to write debug text to file.

public class Framework {

    public IList<CompositionPlugin> CompositionPlugins = new List<CompositionPlugin>();

    public CompositionPlugin GetCompositionPlugin(Type ofType)
    {
        using(var writer = System.IO.File.CreateText(@"C:\test.log"))
        {
            writer.WriteLine("ofType: " + ofType.toString());
            foreach (CompositionPlugin plugin in CompositionPlugins)
            {
                writer.WriteLine("plugin: " + plugin.GetType().toString());
                if (plugin.GetType().Equals(ofType))
                    return plugin;
            }
        }

        throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
    }
}
SynerCoder
  • 12,493
  • 4
  • 47
  • 78
  • 1
    Your solution doesn't work because `is` requires the actual Type, which I don't have. I have a variable. – F.P Aug 01 '12 at 14:15
  • As @vcsjones said, this won't work, but it won't work for two reasons. 1. You need the type name and not instance of Type. 2. `is` is not like `Equals`. `is` is exactly or inherit form. `Equals` is exactly only. – Amiram Korach Aug 01 '12 at 14:16
  • In your the version you check if the type is exactly the same. In mine I check if it is that type, meaning that when class B extends A, an object of type B is typeof(A) – SynerCoder Aug 01 '12 at 14:16
1

Your problem is most likely due to the fact that the assembly is loaded a second time and therefor the equality fails, it's best to avoid the assembly being loaded again, so you can use normal type comparison instead of needing to use that dirty string comparison. I had the same problem, my plugin interface was defined in a separate assembly, this assembly was present in the start up folder but also in the plugin folder from which the plugin assemblies where dynamically loaded, by consequence interface type equality failed. I removed the interface dll from the plugin folder and after that everything worked as expected.

0

Turns out, the information I initially left out of the question, deeming it not important, was so after all.

The MyCompositionPlugin and CompositionPlugin are both defined in different assemblies, that the executing program loads dynamically at runtime.

The .NET-Runtime now consideres a type loaded from a different assembly another than the one referenced by the executing assembly, rendering the MyCompositionPlugin-Type in the Program to be considered unequal to the MyCompositionPlugin loaded from another assembly, even if they are actually the same.

The solution to comparing the two for equality (in that they are the same "Class" in the common sense) is to break it down to a string-equality of the defining assemblies, which is arguably dirty, but does the trick.

public CompositionPlugin GetCompositionPlugin(Type ofType)
{
    foreach (CompositionPlugin plugin in CompositionPlugins)
        if (ofType.AssemblyQualifiedName.Equals(plugin.GetType().AssemblyQualifiedName))
            return plugin;

    throw new ArgumentException("A composition plugin of type " + ofType.FullName + " could not be found");
}

Cudos to Paolo Falabella who pointed to this question

Community
  • 1
  • 1
F.P
  • 17,421
  • 34
  • 123
  • 189