2012-09-28 33 views
0

我试图重构一段代码,并用尽了我能想到的选项。如何实例化一个COM类接口一般

这是原来的代码,我有:

 if (WebConfigSettings.ComPartition == null && HttpContext.Current != null) 
      Nses = new NSession(); 
     else 
      Nses = (INSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.NSession", WebConfigSettings.ComPartition)); 

 if (WebConfigSettings.ComPartition == null && HttpContext.Current != null) 
      apses.Wses = new WSession(); 
     else 
      apses.Wses = (IWSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.WSession", WebConfigSettings.ComPartition)); 

这是怎么我试图重构它:
(是的,在C#中,你可以instantiateaninterface

public static TInterface Get<TSubInterface, TInterface>() where TSubInterface: TInterface 
    { 
     <snip></snip> 
     if (!useComPartitions) 
      return Activator.CreateInstance<TSubInterface>(); // --> this is not cooperating 

     return (TInterface)Marshal.BindToMoniker(.....); 
    } 

这是我已经尝试过:

  1. 我试图指定新的()约束,然后做一个“新TSubInterface()”: 这将导致生成错误:” ..must是一个非抽象用公共无参数构造函数键入,以便将其用作通用类型或方法中的参数'TSubInterface'。“

  2. 当我使用Activator.CreateInstance时,我得到一个运行时异常:”无法创建一个实例接口“

  3. 当我使用Activator.CreateComInstanceFrom(”someAssemblyName“,”t ypeName“),我得到一个编译错误:‘不能转换表达式类型‘System.Runtime.Remoting.ObjectHandle’返回类型TInterface’

[编辑]我是能够使加入该编译'在哪里TSubInterface:class,但我不确定这是否合理,因为TSubInterface是一个接口。
使用CreateComInstanceFrom也不起作用,因为它试图找到在dll不是并且不应该是的目录中指定的程序集。

我可以以某种方式使这个编译和运行?

+0

你使用这个方法的哪个代码? –

+0

nses = COMUtils.Get () NSession和INSession都是接口。 – TweeZz

+1

你想让它工作只是因为你可以或者你打算在生产代码中使用它?如果另外一个人能够通过控制容器的反转来达到类似的目的,那么他们会更好。 – Rafal

回答

3

您需要关注能够从接口名称创建类对象的看似魔术。让我们挑选一个每个人都可以尝试的例子。创建一个新的控制台应用程序并使用Project + Add Reference,Browse选项卡并选择c:\ windows \ system32 \ shell32.dll。

查看使用Object Browser生成的互操作库。注意Shell类型是一种接口类型。现在编写这段代码:

class Program { 
    static void Main(string[] args) { 
     var shl = new Shell32.Shell(); 
    } 
} 

编译并在.exe文件上运行ildasm.exe。你会看到:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  8 (0x8) 
    .maxstack 1 
    .locals init ([0] class [Interop.Shell32]Shell32.Shell 'shl') 
    IL_0000: nop 
    IL_0001: newobj  instance void [Interop.Shell32]Shell32.ShellClass::.ctor() 
    IL_0006: stloc.0 
    IL_0007: ret 
} // end of method Program::Main 

注类型名称如何得到取代从壳牌ShellClass。类型库导入器创建该类,它使用原始的coclass名称并将“Class”附加到该名称。编译器进行替换。

哪个是关键,Activator.CreateInstance()不能进行相同的替换。我没有看到明确的方式让泛型进行相同的替换,而不是直接使用IFooClass名称而不是接口名称。从技术上讲,您可以检索类型库导入器应用于接口类型的[CoClass]属性。

+0

将您的标记标记为正确,因为它提出了一个解决方案(使用'CoClass'属性),并解释了一些原因并不像我希望的那样容易 – TweeZz

0

可以搞清楚那是什么接口的组件类和创造者的一个实例来完成:

var coClassAttribute = type.GetCustomAttribute<CoClassAttribute>(); // our extension method 
return (TSubInterface)Activator.CreateInstance(coClassAttribute.CoClass); 

我并不满足于此,但它的作品。 (不会将此标记为正确答案)