动态加载具有相同函数的不同库

时间:2017-11-16 13:12:41

标签: c# reflection

我有一个程序,它引用了一个外部DLL:

magic.externalFunctions.sql.dll

取决于目标系统,我可能需要以下内容:

magic.externalFunctions.nosql.dll

这些DLL是提供相同功能的外部库。即使是公共方法和属性也是一样的。但是他们具有组合的接口或基类。

现在我需要根据目标系统在这两个库之间进行选择。如果他们拥有一个共同的接口,我会做类似的事情:

public class DoDatabaseStuff() {
    private IMagic _magic
    public DoDatabaseStuff(bool useNoSql) {
       if (useNoSql) {
         _magic=new SqlMagic();
       } else {
         _magic=new NoSqlMagic();
       }
       myTable=_magic.Table.Create("CustomerTable");
       myTable.Columns.Add(typeof(int),"ID");
       myTable.Columns.Add(typeof(string),"Name");
   }
}

有没有办法在不使用过量反射的情况下解决这个问题?我仍然希望使用类似myTable.Columns.Add()而不是tableType.GetMethod("Add").Invoke()...

的内容

我也不想使用不同的构建配置在csproj中使用条件。虽然这在理论上确实有效,但每次csproj自动更改时都会被破坏(例如,nuget-package更新)

1 个答案:

答案 0 :(得分:5)

如果您遇到了无法改变的糟糕设计,那么最好的方法就是将该设计从代码中删除。在这种情况下,这意味着您应该创建外部程序集缺少的公共接口。

您可以使用适配器提供统一的界面,以避免初始设计缺陷超出代码核心。

首先要为要访问的原始类创建抽象:

public interface IMagic {
    ITable CreateTable(string name);
}

public interface ITable {
    void AddColumn(Type type, string name);
}

然后你提供适配器:

class SqlMagicAdapter : IMagic {
    SqlMagic m_innerMagic = new SqlMagic();

    ITable CreateTable(string name) {
        return new SqlTableAdapter(m_innerMagic.Table.Create(name));
    }
}

class SqlTableAdapter : ITable {
    SqlTable m_innerTable;
    public SqlTableAdapter(SqlTable innerTable) {
        m_innerTable = innerTable;
    }
    void AddColumn(Type type, string name) {
        m_innerTable.Columns.Add(type, name);
    }
}

class NoSqlMagicAdapter : IMagic {
    NoSqlMagic m_innerMagic = new NoSqlMagic();

    ITable CreateTable(string name) {
        return new NoSqlTableAdapter(m_innerMagic.Table.Create(name));
    }
}

class NoSqlTableAdapter : ITable {
    NoSqlTable m_innerTable;
    public NoSqlTableAdapter(NoSqlTable innerTable) {
        m_innerTable = innerTable;
    }
    void AddColumn(Type type, string name) {
        m_innerTable.Columns.Add(type, name);
    }

使用工厂方法,您可以返回相应的适配器:

public static class MagicFactory {
    public static IMagic GetMagic(bool useNoSql) {
        if (useNoSql) {
            return new NoSqlMagic();
        }
        else {
            return new SqlMagic();
        }
    }
}

只使用工厂返回的抽象,您的核心代码可以保持清洁:

public class DoDatabaseStuff() {
    private IMagic _magic
    public DoDatabaseStuff(bool useNoSql) {
       _magic = MagicFactory.GetMagic(useNoSql);
       ITable myTable = _magic.CreateTable("CustomerTable");
       myTable.AddColumn(typeof(int), "ID");
       myTable.AddColumn(typeof(string), "Name");
   }
}

另一个优势是您还可以为将来的任何更改做好准备,例如,另一个更新的外部库与之前的更新不兼容。您可以轻松支持这些新功能,而无需更改核心代码。