programming/netfx/fcl/System.Runtime.InteropServices.CoClassAttribute/index.wiki.txt

current previous
1,341 0,0
+
%smdncms%(set-metadata,title,System.Runtime.InteropServices.CoClassAttribute)
+
%smdncms%(set-metadata,alternative-document-versions,codelang=cs,codelang=vb)
+

         
+
*CoClass属性
+
&msdn(netfx,type,System.Runtime.InteropServices.CoClassAttribute){CoClassAttribute};の本来の目的であるCOM interopはさておき、CoClass属性を使うと、次の例のように見かけ上インターフェイスを直接``new``する、インターフェイスに対して''デフォルト実装クラス''を定義するようなことができる。
+

         
+
#tabpage(codelang=cs,container-title=CoClassAttributeを使ってインターフェイスのデフォルト実装クラスを定義する)
+
#code{{
+
using System;
+
using System.Runtime.InteropServices;
+

         
+
[CoClass(typeof(C))] // クラスCをインターフェイスIのコクラスとして定義する
+
[ComImport]
+
[Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")]
+
interface I {
+
  void M();
+
}
+

         
+
// インターフェイスIを実装するクラス
+
class C : I {
+
  public void M() => Console.WriteLine("Hello, world!");
+
}
+

         
+
public static class Sample {
+
  static void Main()
+
  {
+
    // インターフェイスIに対してnewする
+
    var i = new I();
+

         
+
    // 作成したインスタンスのメソッドMを呼び出す
+
    i.M();
+

         
+
    // インスタンスiの型情報を取得する (クラスCの型情報が取得される)
+
    Console.WriteLine($"type of {nameof(i)} is {i.GetType()}");
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
+

         
+
<
+
  CoClass(GetType(C)), ' クラスCをインターフェイスIのコクラスとして定義する
+
  ComImport,
+
  Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")
+
>
+
Interface I
+
  Sub M()
+
End Interface
+

         
+
' インターフェイスIを実装するクラス
+
Class C
+
  Implements I
+

         
+
  Public Sub M() Implements I.M
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+

         
+
Public Module Sample
+
  Sub Main()
+
    ' インターフェイスIに対してNewする
+
    Dim i As New I()
+

         
+
    ' 作成したインスタンスのメソッドMを呼び出す
+
    i.M()
+

         
+
    ' インスタンスiの型情報を取得する (クラスCの型情報が取得される)
+
    Console.WriteLine($"type of {NameOf(i)} is {i.GetType()}")
+
  End Sub
+
End Module
+
}}
+
#tabpage-end
+

         
+
#prompt(実行結果){{
+
Hello, world!
+
type of i is C
+
}}
+

         
+
インターフェイス``I``に対する``new``は、コンパイル時において、CoClass属性で指定されたコクラス``C``に対する``new``に置き換えられる。
+

         
+

         
+

         
+
シグニチャがマッチする、かつアクセス可能なコンストラクタが存在する場合は、引数付きで``new``することもできる。 引数リストと一致するアクセス可能なコンストラクタがなければ''コンパイルエラー''となる(実行時例外ではない)。
+

         
+
#tabpage(codelang=cs,container-title=CoClassAttributeで定義したデフォルト実装クラスと引数付きのコンストラクタ)
+
#code{{
+
using System;
+
using System.Runtime.InteropServices;
+

         
+
[CoClass(typeof(C))]
+
[ComImport]
+
[Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")]
+
interface I {
+
  void M();
+
}
+

         
+
class C : I {
+
  // 引数を取るコンストラクタ
+
  public C(int x, string y, object z) => Console.WriteLine($"x = {x}, y = {y}, z = {z}");
+

         
+
  public void M() => Console.WriteLine("Hello, world!");
+
}
+

         
+
public static class Sample {
+
  static void Main()
+
  {
+
    new I(42, "foo", "bar");
+
  }
+
}
+

         
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Runtime.InteropServices
+

         
+
<
+
  CoClass(GetType(C)),
+
  ComImport,
+
  Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")
+
>
+
Interface I
+
  Sub M()
+
End Interface
+

         
+
Class C
+
  Implements I
+

         
+
  Public Sub New(ByVal x As Integer, ByVal y As String, ByVal z As Object)
+
    Console.WriteLine($"x = {x}, y = {y}, z = {z}")
+
  End Sub
+

         
+
  Public Sub M() Implements I.M
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+

         
+
Public Module Sample
+
  Sub Main()
+
    Dim i As New I(42, "foo", "bar")
+
  End Sub
+
End Module
+
}}
+
#tabpage-end
+

         
+
#prompt(実行結果){{
+
x = 42, y = foo, z = bar
+
}}
+

         
+

         
+

         
+
アセンブリ境界を跨ぐ場合、コクラスにアクセスできなければ''コンパイルエラー''となる(実行時例外ではない)。 このため、ライブラリにてインターフェイスとデフォルト実装(CoClass属性)のみを公開しつつ、デフォルト実装クラスは非公開とする、といったことはできない。
+

         
+
#code(cs,ライブラリ側コード){{
+
using System;
+
using System.Runtime.InteropServices;
+

         
+
[assembly: System.Reflection.AssemblyTitle("Library")]
+

         
+
namespace Library {
+
   // コクラスを指定したpublicなインターフェイス
+
  [CoClass(typeof(C))]
+
  [ComImport]
+
  [Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")]
+
  public interface I {
+
    void M();
+
  }
+

         
+
  // コクラス(internalなクラス)
+
  internal class C : I {
+
    public void M() => Console.WriteLine("Hello, world!");
+
  }
+
}
+
}}
+

         
+
#code(cs,アプリケーション側コード){{
+
// <ProjectReference Include="Library.csproj" />
+

         
+
using System;
+

         
+
[assembly: System.Reflection.AssemblyTitle("Application")]
+

         
+
namespace Application {
+
  public static class Sample {
+
    static void Main()
+
    {
+
      var i = new Library.I(); // error CS0122: 'C.C()' はアクセスできない保護レベルになっています
+

         
+
      i.M();
+
    }
+
  }
+
}
+
}}
+

         
+

         
+
**ComImport属性とGuid属性
+
C#でCoClass属性をインターフェイスに付与する場合、&msdn(netfx,type,System.Runtime.InteropServices.ComImportAttribute){ComImport属性};と&msdn(netfx,type,System.Runtime.InteropServices.GuidAttribute){Guid属性};も同時に付与する必要がある。 Guid属性で指定するGUIDは適当でよい(COM importを目的としないのであれば)。
+

         
+
#code(cs,C#では、CoClass属性はComImport属性とGuid属性とともに指定する必要がある){{
+
[CoClass(typeof(C))]
+
/*[ComImport]*/ // warning CS0684: 'I' インターフェイスは、'CoClassAttribute' でマークされていますが、'ComImportAttribute' ではマークされていません。
+
/*[Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")]*/ // error CS0596: Guid 属性は ComImport 属性を使って指定する必要があります。
+
interface I {
+
  void M();
+
}
+
}}
+

         
+

         
+

         
+
VBでCoClass属性をインターフェイスに付与する場合は、ComImport属性とGuid属性は必ずしも付与する必要がなく、コンパイル時に自動的に付与されるわけでもない模様。
+

         
+
#code(vb,VBでは、CoClass属性は必ずしもComImport属性とGuid属性を伴っていなくてもよい){{
+
Imports System
+
Imports System.Runtime.InteropServices
+
Imports System.Reflection
+

         
+
<CoClass(GetType(C))> '  ComImport属性とGuid属性がなくてもコンパイルできる
+
Interface I
+
  Sub M()
+
End Interface
+

         
+
Class C
+
  Implements I
+

         
+
  Public Sub M() Implements I.M
+
    Console.WriteLine("Hello, world!")
+
  End Sub
+
End Class
+

         
+
Public Module Sample
+
  Sub Main()
+
    Dim i As New I()
+

         
+
    Console.WriteLine($"Guid = {i.GetType().GetCustomAttribute(Of GuidAttribute)()}")
+
    Console.WriteLine($"ComImport = {i.GetType().GetCustomAttribute(Of ComImportAttribute)()}")
+
  End Sub
+
End Module
+
}}
+

         
+
#prompt(実行結果){{
+
Guid = 
+
ComImport = 
+
}}
+

         
+

         
+
**インターフェイスメソッドのデフォルト実装との比較
+

         
+
#column(layout=fixed)
+
#code(cs,CoClassによるデフォルト実装クラス){{
+
using System;
+
using System.Runtime.InteropServices;
+

         
+
// デフォルト実装クラスを持つインターフェイス
+
[CoClass(typeof(C))]
+
[ComImport]
+
[Guid("2964ca28-75b5-4eba-9472-e2aa977b5cf3")]
+
interface I {
+
  void M();
+
}
+

         
+
// インターフェイスIを実装するクラス
+
class C : I {
+
  public void M() => Console.WriteLine("Hello, world!");
+
}
+

         
+
public static class Sample {
+
  static void Main()
+
  {
+
    // インターフェイスIに対してnewする
+
    var i = new I();
+

         
+
    // 作成したインスタンスのメソッドMを呼び出す
+
    i.M();
+

         
+
    // インスタンスiの型情報を取得する
+
    Console.WriteLine($"type of {nameof(i)} is {i.GetType()}");
+
  }
+
}
+
}}
+

         
+
#prompt(実行結果){{
+
Hello, world!
+
type of i is C
+
}}
+
#column
+
#code(cs,インターフェイスメソッドのデフォルト実装){{
+
using System;
+

         
+

         
+
// メソッドのデフォルト実装を持つインターフェイス
+

         
+

         
+

         
+
interface I {
+
  void M() => Console.WriteLine("Hello, world!");
+
}
+

         
+
// インターフェイスIを実装するクラス
+
class C : I {
+

         
+
}
+

         
+
public static class Sample {
+
  static void Main()
+
  {
+
    // クラスCのインスタンスを作成し、インターフェイスIに変換する
+
    I i = new C();
+

         
+
    // 作成したインスタンスのメソッドMを呼び出す
+
    i.M();
+

         
+
    // インスタンスiの型情報を取得する
+
    Console.WriteLine($"type of {nameof(i)} is {i.GetType()}");
+
  }
+
}
+
}}
+

         
+
#prompt(実行結果){{
+
Hello, world!
+
type of i is C
+
}}
+
#column-end
+

         
+

         
+
*参考
+
#relevantdocs(参考)
+

         
+
-[[C# 9: newキーワードの型推論:https://www.infoq.com/jp/news/2020/07/csharp-9-new/]]
+
-[[COM の CoClass について調べた - しばやん雑記:https://blog.shibayan.jp/entry/20080101/1199114777]]
+

         
+
#relevantdocs-end
+

         
+
#relevantdocs
+

         
+
-[[programming/tips/createlnk]]
+
-[[programming/tips/axwebbrowser]]
+

         
+
#relevantdocs-end
+