将对象转换为从另一个程序集输入

时间:2016-05-11 09:24:11

标签: c# .net dll reflection .net-assembly

我有3个项目:2个是类库,1个是控制台应用程序:

  1. 类库" AnimalManagers"。在AnimalManagers中是

    public interface AnimalAgressiveBase{
          string Bark<TAnimal>(TAnimal animal);
    }
    public abstract class AnimalAgressive<TEnemy> : AnimalAggresiveBase {
          public abstract string BarkAtEnemy(TEnemy enemy);
    
          public string Bark<TAnimal>(TAnimal animal){
                return BarkAtEnemy((TEnemy)animal);// <---- Invalid cast exception even if type of object "animal" is Cat and we call this method from Dog class
          }
    }
    
    public class AnimalManager {
    
       public void WatchAnimal<TAnimalModel>
           (TAnimalModel animalModel) { //<-- in example, animalModel is Cat("lucy")
    
              var aggresiveAnimalDog = GetAgresiveAnimal(); //<-- returns object from dynamically loaded class Dog so this is basicaly new Dog() if animal models library class were referenced
              if(aggresiveAnimalData != null) aggresiveAnimalDog .Bark(animalModel);
       }
       public AnimalAgressiveBase GetAgresiveAnimal(){
           Assembly.LoadFrom("..\\AnimalModels.dll");
           ....
           return aggresiveAnimal; //In This case Dog because he implements correct interface
       }
    }
    
  2. 类库&#34; AnimalModels&#34;在AnimalModels中是模型类Cat和Dog

    public class Cat {
         public string Name {get;set;}
    
    }
    
    public class Dog : AnimalAgressive<Cat> {
        public override string BarkAtEnemy(Cat enemy){return enemy.Name;}
    }
    
  3. 两个库都在ConsoleApp&#34; Zoo&#34; ,当我调用

    时,我得到无效的类型异常
    var manager = new AnimalManager();
    var manager.WatchAnimal(new Cat{Name="lucy"});
    

    我得到了无效的强制转换异常,因为动态加载的程序集类型与引用的类型不相同,即使它们具有相同的AssemblyQualifiedName。

    任何解决方案?目标是加载实现已知接口的汇编获取类型(例如:AnimalAggressiveBase)和调用我传递某种类型数据的方法。

4 个答案:

答案 0 :(得分:0)

您的主要问题似乎是您在类型系统中没有代表您的动物的任何东西。你的班级Cat如何与班级的其他层级结合?

public void WatchAnimal<TAnimalModel>
   (TAnimalModel animalModel) { ... }

基本上TAnimalModel可以是任何;我可以传递string intStream,编译器也不会抱怨。您需要将TAnimalModel保持为非常期望的内容,以便编译器可以推断您可以对animalModel做什么或不做什么:

public void WatchAnimal<TAnimalModel>
   (TAnimalModel animalModel) { ... } where TAnimalModel: AnimalBase

AnimalBase可以是一个抽象基类,一个常规基类,一个接口,你有什么。唯一的条件是你所有的动物模型都需要实现它:

public class Cat: AnimalBase { ... }.

使用以下方法也会发生同样的事情:

public abstract class AnimalAgressive<TEnemy> : AnimalAggresiveBase { ... }

同样,这里TEnemy可以是任何东西。这就是您在(TEnemy)animal中收到编译时错误的原因。编译器无法验证转换是否有效,因此不允许它; 知道TEnemy将成为动物,但你还没有告诉编译器...所以告诉编译器添加一个constaint:

public abstract class AnimalAgressive<TEnemy> : AnimalAggresiveBase where TEnemy: AnimalBase { ... }

答案 1 :(得分:0)

性能昂贵的解决方案恕我直言。

[EDIT TO CLASS]

public abstract class AnimalAgressive<TEnemy> : AnimalAggresiveBase where TEnemy : class, new(){

[EDIT TO METHOD]
public string Bark<TAnimal>(TAnimal animal){
            var typeAnimal = typeof(TAnimal);
            var typeEnemy = typeof(TEnemy);

            var propsAnimal = typeData.GetProperties();
            var propsEnemy = typeGener.GetProperties();

            var obj = new T();

            foreach (var propertyInfo in propsAnimal)
            {
                var value = propertyInfo.GetValue(dataObject);
                try
                {
                    var propEnemy  = propsEnemy.FirstOrDefault(x => x.Name.Equals(propertyInfo.Name, StringComparison.CurrentCulture));
                    propGener?.SetValue(obj, value, null);
                }
                catch (Exception)
                {
                    throw;
                }
            }

            return Bark(obj.Name);

答案 2 :(得分:0)

我会在这里推荐MEF

public interface IAnimalAgressive<in TAnimal>
{
    string Bark(TAnimal animal);
}

public abstract class AnimalAggressiveBase<TEnemy> : IAnimalAgressive<TEnemy>
{
    public abstract string BarkAtEnemy(TEnemy enemy);
    public string Bark(TEnemy enemy)
    {
        return BarkAtEnemy(enemy);
    }
}

你的动物经理然后变成:

public class AnimalManager
{
    public void WatchAnimal<TAggressiveAnimal, TAnimal>
        (TAggressiveAnimal aggressive, TAnimal enemy)
        where TAggressiveAnimal : IAnimalAgressive<TAnimal>
    {
        Console.WriteLine(aggressive.Bark(enemy));
    }
}

现在它的责任是Dog和Cat符合AnimalManager的期望,我现在会保持简单:

using System.ComponentModel.Composition;
namespace AnimalModels
{
    [Export(typeof(Cat))]
    public class Cat
    {
        public string Name { get; set; }
    }
}

和狗变成:

using AnimalManagers;
using System.ComponentModel.Composition;
namespace AnimalModels
{
    [Export(typeof(AnimalAggressiveBase<Cat>))]
    public class Dog : AnimalAggressiveBase<Cat>
    {
        public override string BarkAtEnemy(Cat enemy)
        {
            return enemy.Name;
        }
    }
}

现在的诀窍是编写相关类型的整个目录,因此对于我们的控制台应用程序,我们创建了另一种类型,用于从我们导出的类型目录中管理这些关系:

public class TypeManager
{
    public void Compose()
    {
        try
        {
            var directoryPath = Path.GetFullPath(".");
            var aggregateCatalog = new AggregateCatalog();
            aggregateCatalog.Catalogs.Add(new DirectoryCatalog(directoryPath, "*.dll"));

            //Create the composition container
            var container = new CompositionContainer(aggregateCatalog);

            container.ComposeParts(this);

            var cat = container.GetExportedValue<Cat>();
            cat.Name = "lucy";
            var dog = container.GetExportedValue<AnimalAggressiveBase<Cat>>();
            var manager = new AnimalManager();
            manager.WatchAnimal(dog, cat);
        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
            throw;
        }


    }
}

现在我们需要做的就是像这样运行Main方法:

static void Main(string[] args)
    {
        TypeManager tm = new TypeManager();

        tm.Compose();
    }

现在导出的类型将自动在typemanager中创建,我们不必担心反射。

答案 3 :(得分:-1)

public interface AnimalAgressiveBase<TAnimal>
{
    string Bark(TAnimal animal);
}

public abstract class AnimalAgressive<TEnemy, TAnimal> : AnimalAgressiveBase<TAnimal> where TEnemy : TAnimal
{
    public abstract string BarkAtEnemy(TEnemy enemy);

    public string Bark(TAnimal animal)
    {
        return BarkAtEnemy((TEnemy)animal);
    }
}