Search code examples
dynamicreflectionmeflazy-evaluation

List of DisposableLazy`2 does not have 'Add' method when called using dynamic variable


Problem

I am facing a problem using dynamically created list of items when Add method is called on dynamicvariable. Consider following code.

IEnumerable<dynamic> plugins = (IEnumerable<dynamic>)field.GetValue(instance);
if (plugins == null)
  continue;

dynamic filteredPlugins = null;
foreach (var plugin in plugins)
{
  if (filteredPlugins == null)
  filteredPlugins = Activator
    .CreateInstance(typeof(List<>)
    .MakeGenericType(plugin.GetType()));

if (/* this condition does not matter*/)
  //filteredPlugins.Add(plugin);
  filteredPlugins.GetType().GetMethod("Add")
    .Invoke(filteredPlugins, new object[] { plugin });
}

And now, the commented line filteredPlugins.Add(plugin) will throw System.Reflection.TargetInvocationException with the message 'object' does not contain a definition for 'Add' when plugin is of type

System.ComponentModel.Composition.ExportServices.DisposableLazy<IPlugin,IMetadata>

but it works completely perfect when pluginis of type

System.Lazy<IPlugin, IMetadata>

When the reflection is used to call Add method on the instance filteredPlugins instance as is done on the next line - everything works fine for any type.

My question is WHY is not Add method found in case of DisposableLazy type.

Background

This code is part of the method that I use in OnImportsSatisfied() method. I am using two kinds of import - which differs only in RequiredCreationPolicy - on has CreationPolicy.NonShared and the other default value of CreationPolicy.Any.

[ImportMany(RequiredCreationPolicy = CreationPolicy.NonShared)]
private IEnumerable<Lazy<IPlugin, IMetadata>> plugins = null;

For CreationPolicy.NonShared fields the underlaying type in the plugins is DisposableLazy and for CreationPolicy.Any the underlaying type in the plugins is Lazy.

Edit: As asked in the answer - I am using dynamic variable because IPlugin interface can change everytime this method is called and they do not have to have anything in common.

Edit2: I just found similar question C# dynamic type gotcha, so this can be probably closed as duplicite.


Solution

  • Because System.ComponentModel.Composition.ExportServices.DisposableLazy is a private class, the runtime binder is having trouble believing you have permission to use type, where reflection doesn't care.

    Which begs the question why are you using dynamics at all in this case. Since DisposableLazy<IPlugin,IMetadata> public interface is it's subclass Lazy<IPlugin, IMetadata> & IDisposable, shouldn't you just be using a List<Lazy<IPlugin, IMetadata>> for either case?

    var plugins = (IEnumerable<Lazy<IPlugin, IMetadata>>)field.GetValue(instance);
    if (plugins == null)
      continue;
    
    var filteredPlugins = new List<Lazy<IPlugin, IMetadata>>();
    foreach (var plugin in plugins)
    {
      if (/* this condition does not matter*/)
         filteredPlugins.Add(plugin);
      }
    }