class A
{
}
class B : A
{
}
これらのクラスそれぞれに対し、暗黙的に型変換可能なクラス、A1 クラスと B1 クラスを用意するとします。
class A1
{
public A1(A source)
{
this._source = source;
}
private readonly A _source;
public static implicit operator A(A1 target)
{
return target._source;
}
public static implicit operator A1(A target)
{
return new A1(target);
}
}
class B1
{
public B1(B source)
{
this._source = source;
}
private readonly B _source;
public static implicit operator B(B1 target)
{
return target._source;
}
public static implicit operator B1(B target)
{
return new B1(target);
}
}
A1 と B1 は継承関係にありませんが、A と B は継承関係にあります。
そこで、次のように B1 のインスタンスを A1 に型変換できるようにしたいとします。ただし、A1 と B1 はお互いの存在を直接知らないものとします。更に、A1 は A の派生クラスの存在を直接知らないものとします。
B1 b = new B();
A1 a = b;
A1 と B1 はお互いの存在を直接知らないので、まずは間接的に知ることができるよう、A1 と B1 が共通で実装するインターフェイスを用意します。
interface ITypeDef<out T>
{
T Source
{
get;
}
}
このインターフェイスを実装したコードは次のようになります。
class A1 : ITypeDef<A>
{
public A1(A source)
{
this._source = source;
}
protected readonly A _source;
public static implicit operator A(A1 target)
{
return target._source;
}
public static implicit operator A1(A target)
{
return new A1(target);
}
A ITypeDef<A>.Source
{
get
{
return this._source;
}
}
}
class B1 : ITypeDef<B>
{
public B1(B source)
{
this._source = source;
}
protected readonly B _source;
public static implicit operator B(B1 target)
{
return target._source;
}
public static implicit operator B1(B target)
{
return new B1(target);
}
B ITypeDef<B>.Source
{
get
{
return this._source;
}
}
}
ITypeDef<T> の型パラメータ T は共変ですので、ITypeDef<B> を ITypeDef<A> に型変換することが可能です。ということは、A1 が ITypeDef<A> からの変換をサポートすれば、ITypeDef<B> を実装する B1 からの変換ができるようになるはずです。
class A1 : ITypeDef<A>
{
public A1(A source)
{
this._source = source;
}
protected readonly A _source;
public static implicit operator A(A1 target)
{
return target._source;
}
public static implicit operator A1(A target)
{
return new A1(target);
}
public static implicit operator A1(ITypeDef<A> target)
{
return new A1(target.Source);
}
A ITypeDef<A>.Source
{
get
{
return this._source;
}
}
}
class B1 : ITypeDef<B>
{
public B1(B source)
{
this._source = source;
}
protected readonly B _source;
public static implicit operator B(B1 target)
{
return target._source;
}
public static implicit operator B1(B target)
{
return new B1(target);
}
public static implicit operator B1(ITypeDef<B> target)
{
return new B1(target.Source);
}
B ITypeDef<B>.Source
{
get
{
return this._source;
}
}
}
残念ながら、このコードはコンパイルが通りません。なぜか C# ではインターフェイスからの変換演算子の定義が禁止されているからです。
さて、今回の目的は恐らく正攻法では実現できません。
ということで、これから非実用的な実現方法を紹介します。
先にも記述しました通り、変換演算子で変換元または変換先をインターフェイスにすることはできません。
そこでジェネリックの型パラメータを利用します。ジェネリックの型パラメータは変換元や変換先に指定することができます。そして、型引数としてインターフェイスを指定することで、間接的に変換元または変換先をインターフェイスにすることができます。
例えば、次のコードでは IEnumerable<int> から Class1<IEnumerable<int>> への変換が実現されます。
using System.Collections.Generic;
class Class1<T>
{
public Class1(T source)
{
this._source = source;
}
private readonly T _source;
public static implicit operator Class1<T>(T target)
{
return new Class1<T>(target);
}
}
class Program
{
static void Main(string[] args)
{
Class1<IEnumerable<int>> c = new List<int>();
}
}
Class1<T> の型パラメータに IEnumerable<int> と指定していますので、変換演算子は実質次のような形と同等になります。
public static implicit operator Class1<IEnumerable<int>>(IEnumerable<int> target)
{
return new Class1<IEnumerable<int>>(target);
}
では、話を元に戻して ITypeDef<A> から A1 への変換を実現してみます。
A1 はあくまでも非ジェネリッククラスのままにしたいので、先ほどの Class1<T> よりも更に複雑になります。
A1<T> クラスを用意して、これを A1 が継承するようにします。
A1<T> は A1 のためだけに存在しますので、A1 の定義以外のコードからは直接使用しないようにします。
abstract class A1<T>
where T : ITypeDef<A>
{
public static implicit operator A1<T>(T target)
{
return (A1<T>)(object)new A1(target.Source);
}
}
class A1 : A1<ITypeDef<A>>, ITypeDef<A>
{
public A1(A source)
{
this._source = source;
}
protected readonly A _source;
public static implicit operator A(A1 target)
{
return target._source;
}
public static implicit operator A1(A target)
{
return new A1(target);
}
A ITypeDef<A>.Source
{
get
{
return this._source;
}
}
}
B1 についても同じようにしておきます。
abstract class B1<T>
where T : ITypeDef<B>
{
public static implicit operator B1<T>(T target)
{
return (B1<T>)(object)new B1(target.Source);
}
}
class B1 : B1<ITypeDef<B>>, ITypeDef<B>
{
public B1(B source)
{
this._source = source;
}
protected readonly B _source;
public static implicit operator B(B1 target)
{
return target._source;
}
public static implicit operator B1(B target)
{
return new B1(target);
}
B ITypeDef<B>.Source
{
get
{
return this._source;
}
}
}
ところで、A1<T> に定義されている変換演算子は、あくまでも A1 ではなく A1<T> への変換演算子になっています。
つまり、B1 から A1 に変換する際の内部的な流れとしては B1 → A1<T> → A1 という二段階の流れになります。
A1<T> から A1 への変換はダウンキャストとなるため明示的な変換が必要になります。
というわけで、B1 から A1 への変換は暗黙的にはできません。次のように明示的な変換となります。
B1 b = new B();
A1 a = (A1)b;
以上で当初の目的は一応達成できました。
仕組みが複雑なので実用度は低いですね。