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

C# 动态加载具有相同函数的不同库,c#,reflection,C#,Reflection,我有一个引用外部DLL的程序: magic.externalFunctions.sql.dll 根据目标系统的不同,我可能需要以下内容: magic.externalFunctions.nosql.dll 这些DLL是提供相同功能的外部库。甚至公共方法和属性也是相同的。但是它们没有组合接口或基类 现在,我需要根据目标系统在这两个库之间进行选择。如果他们有一个共同的接口,我会这样做: public class DoDatabaseStuff() { private IMagic _ma

我有一个引用外部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软件包更新)时,它都会被分发。

当您面临无法更改的糟糕设计时,一个好方法是将该设计保留在代码之外。在这种情况下,这意味着您应该创建外部程序集缺少的公共接口

您可以使用适配器来提供统一的接口,从而将初始设计缺陷排除在代码核心之外

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

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");
   }
}

另一个优点是,您还可以随时进行任何将来的更改,例如,另一个更新的外部库与以前的库不兼容。您可以轻松地支持这些新功能,而无需更改核心代码。

当您遇到无法更改的糟糕设计时,最好的方法是将该设计保留在代码之外。在这种情况下,这意味着您应该创建外部程序集缺少的公共接口

您可以使用适配器来提供统一的接口,从而将初始设计缺陷排除在代码核心之外

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

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");
   }
}

另一个优点是,您还可以随时进行任何将来的更改,例如,另一个更新的外部库与以前的库不兼容。您可以轻松地支持这些新功能,而无需更改核心代码。

为什么要在多个执行相同操作的库之间切换?当然,这是一种低效的方法,而且会增加依赖项的数量。但是,如果需要这样做,您可以始终创建帮助器方法,因此一个方法调用SQL库,另一个方法调用非SQL库创建一个包含基类或接口的程序集,并且只向该程序集引入依赖项。然后可以加载适当的实现程序集。在这些情况下,依赖性注射是一种可行的方法。这里的主要设计缺陷是引入两个独立的程序集。然后,您仍然可以引入一个带有抽象的程序集,并创建两个适配器程序集来引用抽象和相应的外部库。那么你们又有了共同点。您甚至可以在一个程序集中完成所有操作。@Sefe是正确的,不要让他们的设计缺陷永久化。将复杂性抽象出来是一种方法。为什么要在多个做相同事情的库之间切换?当然,这是一种低效的方法,而且会增加依赖项的数量。但是,如果需要这样做,您可以始终创建帮助器方法,因此一个方法调用SQL库,另一个方法调用非SQL库创建一个包含基类或接口的程序集,并且只向该程序集引入依赖项。然后可以加载适当的实现程序集。在这些情况下,依赖性注射是一种可行的方法。这里的主要设计缺陷是引入两个独立的程序集。然后,您仍然可以引入一个带有抽象的程序集,并创建两个适配器程序集来引用抽象和相应的外部库。那么你们又有了共同点。您甚至可以在一个程序集中完成所有操作。@Sefe是正确的,不要让他们的设计缺陷永久化。将复杂性抽象出来是一条出路。