我在应用程序之间有一个.dll分层系统,其中最低层具有一个提供某些功能的类-可以通过GetClass()函数接收该类的实例,然后可以访问其属性(基本上,不断变化的对象的信息的集合)。
现在我注意到,当我想从下一个更高级别的.dll访问该信息时,编译器会抱怨我没有引用较低级别的.dll(定义该类的那个)-实际上,我想避免,以便在我的体系结构中有一个很好的分层结构。
如何解决这个问题?我可以重新公开引用的类型吗?如果我想要完全相同的功能,是否真的需要编写自己的包装程序?还是我什至需要再次引用低级的.dll?
DLL 1:
class myClass;
myClass GetMyClass();
DLL 2:
myClass GetMyClass();
EXE:
如何通过调用GetMyClass(DLL 2)而不引用DLL 1来访问结果?
如果在myClass
中定义了dll1
,似乎没有办法实现,因为类型myClass
的对象可以在运行时实例化。为避免这种情况,您需要在GetMyClass()
中更改dll2
的返回类型,以返回dll2
中定义的内容。它可以是与myClass
非常相似且具有相同属性的类(您甚至可以使用AutoMapper之类的工具轻松地在对象之间进行转换),但绝对应该在dll2
中。类似于:
// dll1
class myClass
{
...
}
myClass GetMyClass()
{
...
}
// dll2
class myClass2
{
public myClass2(myClass c)
{
// instantiate myClass2 with values from myClass
}
}
myClass2 GetMyClass()
{
// somehow get myClass and convert it to myClass2 here
}
您需要将在所有层上使用的所有通用类分离为一个新的dll,然后在每个项目中引用该dll。
尝试使用interfaces,以便您可以处理合同(功能)而不是具体的实现。这将帮助您避免不必要的引用。
// common dll
public interface IMyClass
{
string MyData { get; set; }
IMyClass GetMyClass();
}
// dll1
public class myClass : IMyClass
{
public string MyData { get; set; }
public IMyClass GetMyClass() { return new myClass() { MyData = "abc" }; }
}
// dll2
public class myClass2
{
public IMyClass GetMyClass()
{
var c1 = new myClass();
var c2 = c1.GetMyClass();
return c2;
}
}
// exe (references common and dll2)
public class Program
{
public static void Main(string[] args)
{
var c1 = new myClass2();
IMyClass c2 = c1.GetMyClass();
Console.Writeline(c2.MyData);
}
}
我们在本地代码中执行与此类似的操作。您可以在运行时加载程序集,使用反射扫描程序集所包含的类型,然后再次使用反射调用函数并从该dll中实例化类型,而无需在项目中直接引用它。
您将需要的一些关键功能是:
Assembly.LoadFrom(path); //get the assembly as a local object
Activator.CreateInstance(type); //create an instance of a type
Assembly.GetType(string);//fetch a type by name from the assembly
一旦有了类型,基本的反射就会给您几乎您需要的其他所有内容。
这是我的本地代码段:
asm = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, filePath));
Type[] types = asm.GetTypes();
for (var x = 0; x < types.Length; x++)
{
var interfaces = types[x].GetInterfaces();
for (var y = 0; y < interfaces.Length; y++)
{
if (interfaces[y].Name.Equals("MyTypeName"))
{
isValidmod = true;
var p = (IMyType)Activator.CreateInstance(types[x]);
//Other stuff
}
}
也请注意:这是很旧的代码。多年来,它都没有经过审查或重构,因此从.NET 1.1开始,它实际上是现代的,但是:它说明了这一点。如何从未本地引用的远程程序集中加载类型。
此外,这是引擎的一部分,由于采用刚性文件夹结构,因此可以加载其中的50左右,这就是其外观如此通用的原因。从中获取所需的东西。
调用者必须对DLL1中的类有一个引用,才能知道它正在访问什么类型。因此,是的,您需要引用exe中的第一个dll。由于GetMyClass()返回DLL1中的类型,因此该类型需要在exe中公开,因此必须引用dll1。
我与Nick一起使用spring.net或Microsoft unity之类的任何Ioc框架。为了正确理解这个想法,请通过http://martinfowler.com/articles/injection.html
这里的一个解决方案是提供第4个DLL,其中包含类的接口。您将在所有3层中都引用它,并返回这些接口而不是您的类。
这应该使您对我的意思有所了解:
// DLL1
class ClassInDLL1 : IClassInDLL1
{
}
// DLL2
class ClassInDLL2
{
public IClassInDLL1 GetClassInDLL1()
{
return new ClassInDLL1();
}
}
// DLL3
class ClassInDLL3
{
public void DoSomething()
{
var dll2 = new ClassInDLL2();
var dll1 = dll2.GetClassInDLL1(); // dll1 variable is of type IClassInDLL1
// do stuff with dll1
}
}
// interface DLL
interface IClassInDLL1
{
}
不过,老实说,除非您的项目非常大,否则像这样对体系结构进行分层通常不是一个很棒的主意。我发现,人为地提前进行此类装配拆分会给您带来不必要的痛苦,更不用说您最终为一个可能只需要1个中型或小型项目的3-4个装配体了。