C#と諸々

C#がメインで他もまぁ諸々なブログです
おかしなこと書いてたら指摘してくれると嬉しいです(´・∀・`)
つーかコメント欲しい(´・ω・`)

--/--/-- --:--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
タグ:
トラックバック(-) | コメント(-) | このエントリーを含むはてなブックマーク
2011/06/05 23:57
先日公開した DDD サンプルアプリのアプリケーションアーキテクチャを、フレームワークとして CodePlex で公開しました。

DDDProjector - Home

フレームワークと言っても、クラスライブラリとしてではなく、C# ソリューション (を作成するツール) として提供します。なので、フレームワークのソースコードを直接カスタマイズすることが可能です。
サンプルアプリではデータストアに SQL CE 4.0、プレゼンテーション技術に Windows フォームを使用していましたが、このフレームワークでは特定のデータストア・プレゼンテーション技術には依存していません。

ツールを起動すると、ソリューション名や各プロジェクト名・名前空間の入力フォームが表示されます。
各フィールドを入力後、作成ボタンを押すと保存先選択ダイアログが出てきます。保存先を決定するとそこにソリューションが作成されます。
なお、入力値の妥当性検証は一切行っていないので注意してください。

これから少しずつ解説記事を公開していこうと思います。
スポンサーサイト
2011/05/17 03:01
NHibernate + SQL Server Compact 4.0 + Windows フォームで作った DDD (Domain Driven Design: ドメイン駆動設計) のサンプルアプリを CodePlex で公開してみました。
Sandcastle で作った、アプリケーションアーキテクチャに関する説明ドキュメントも同梱してあります。
# ユニットテストは用意していません。

DDD サンプル - ToDoList - Home

2011/05/04 19:15

  • 列挙型のプロパティをマッピングできない
  • クエリー式で、列挙値が扱えない (int として扱わなければいけない)
  • クエリー式で、エンティティを直接比較できない (主キー等のプロパティを比較しなければいけない)
  • コレクションの順序を保存できない (順序を自分で管理する必要がある)
  • 関連オブジェクトの読み込みが非透過的
  • 関連オブジェクトの遅延読み込みが使いづらい (コレクションだけ遅延読み込みにするとかできない。そのためプロキシ作成を容認しなければいけない)
  • NHibernate の cascade=all-delete-orphan に相当する機能がない (集約内の子エンティティの削除はパーシステンスレイヤへ明示的に指示せずにできるべき)

Entity Framework 4.1 は試してないけど、この辺はそのままっぽい感じ
2番目のはロジックで弾きだした値を列挙型で扱おうとしてダメだった
最後のだけは何とかならんものか…

2011/04/06 10:56
DDD ではエンティティの構築をエンティティ自身が知っているのは不自然ということでファクトリの導入が推奨されているけれど、ファクトリよりビルダの方が向いているんじゃないかなぁ?
ファクトリだと、集約ルートの生成とそれ以外のエンティティの生成はアトミックに扱えないけどビルダならアトミックに扱えますし、構築中は不正な状態も扱えます。
また、構築に関するロジックがエンティティに一切なくなります。

ビルダは特に、UI への入力直後に入力チェックする場合や、ウィザード形式で入力を進めていく場合なんかに本領発揮します。

Person.cs
public sealed partial class Person
{
    private Person(string name, IList<Address> addresses)
    {
        _name = name;
        _addresses = addresses;
    }

    private string _name;

    private readonly IList<Address> _addresses;

    public string Name
    {
        get { return _name; }
    }

    public IEnumerable<Address> Addresses
    {
        get { return _addresses; }
    }
}

Address.cs
public sealed class partial Address
{
    ...
}

Person.Builder.cs
partial class Person
{
    public sealed class Builder
    {
        [Flags]
        public enum States
        {
            Valid = 0,
            NameIsEmpty = 1,
            NameIsTooLong = 2,
            AddressListIsEmpty = 4,
            AddressListHasError = 8,
        }


        public static readonly int MaxNameLength = 20;


        public Builder()
        {
            Name = null;
            AddressBuilders = new List<Address.Builder>();
        }

        public Builder(Person target)
        {
            if (target == null)
            {
                throw new ArgumentNullException("target");
            }
            Name = target.Name;
            AddressBuilders = target._addresses.Select(address => new Address.Builder(address)).ToList();
        }


        private readonly Person _target;

        public string Name;

        public readonly List<Address.Builder> AddressBuilders;


        public Address.Builder AddAddress()
        {
            var addressBuilder = new Address.Builder();
            AddressBuilders.Add(addressBuilder);
            return addressBuilder;
        }

        public void RemoveAddress(int index)
        {
            AddressBuilders.RemoveAt(index);
        }


        public States Validate()
        {
            return (ValidateName() | ValidateAddresses());
        }

        public States ValidateName()
        {
            if (string.IsNullOrWhitespace(Name))
            {
                return States.NameIsEmpty;
            }
            if (MaxNameLength < Name.Length)
            {
                return States.NameIsTooLong;
            }
            return States.Valid;
        }

        public States ValidateAddresses()
        {
            States result = States.Valid;
            if (AddressBuilders.Length == 0)
            {
                result |= States.AddressListIsEmpty;
            }
            foreach (var addressBuilder in AddressBuilders)
            {
                if (addressBuilder.Validate() != Address.Builder.States.Valid)
                {
                    result |= States.AddressListHasError;
                    break;
                }
            }
            return result;
        }


        public Person Build()
        {
            if (Validate() != States.Valid)
            {
                throw new InvalidOperationException();
            }

            var addresses = AddressBuilders.Select(builder => builder.Build()).ToList();

            if (_target == null)
            {
                return new Person(Name, addresses);
            }
            else
            {
                _target._name = Name;
                _target._addresses.Clear();
                foreach (var address in addresses)
                {
                    _target._addresses.Add(address);
                }
                return _target;
            }
        }
    }
}

Address.Builder.cs
partial class Address
{
    public sealed class Builder
    {
        [Flags]
        public enum States
        {
            ...
        }
        internal AddressBuilder() { ... }
        public States Validate() { ... }
        internal Address Build() { ... }
    }
}


まだ色々思案中でこれが本当に良い案なのかどうか確信は持ててないのですが。。。


[関連書籍]


ついにエリック・エヴァンスの "Domain-Driven Design" の日本語版が出版されますよ

2010/06/25 01:08

ガード句っぽい選択

あー本題からそれてるような気もしますが。
あと型名勝手に変えちゃいましたが。

class Generation
{
    public static readonly Generation Over60  = new Generation(60, null);
    public static readonly Generation Over50  = new Generation(50, 59);
    public static readonly Generation Over40  = new Generation(40, 49);
    public static readonly Generation Over30  = new Generation(30, 39);
    public static readonly Generation Over20  = new Generation(20, 29);
    public static readonly Generation Under20 = new Generation(null, 19);

    public static Generation GetGeneration(int age)
    {
        return
            new[]
            {
                Over60,
                Over50,
                Over40,
                Over30,
                Over20,
                Under20,
            }
            .Single(item => item.IsIncludeing(age));
    }


    private Generation(int? lowerLimit, int? upperLimit)
    {
        LowerLimit = lowerLimit;
        UpperLimit = upperLimit;
    }

    public readonly int? LowerLimit;
    public readonly int? UpperLimit;

    public bool IsIncludeing(int age)
    {
        bool isWithinLowerLimit = LowerLimit.HasValue ? (LowerLimit <= age) : true;
        bool isWithinUpperLimit = UpperLimit.HasValue ? (age <= UpperLimit) : true;
        return isWithinLowerLimit && isWithinUpperLimit;
    }
}


元ネタにあるような特定の使い方だけしかしない内は、
こんなクラス化なんてやりすぎですが、
色々な使い方が出てきたときにはこういう方向に進んでいくことかと思います。

ついでに、Static フィールド の種類を増やす時に変更箇所が一箇所で済むよう
リフレクションとカスタム属性を導入してみます。

class Generation
{
    class ItemAttribute : Attribute { }

    [Item]public static readonly Generation Over60  = new Generation(60, null);
    [Item]public static readonly Generation Over50  = new Generation(50, 59);
    [Item]public static readonly Generation Over40  = new Generation(40, 49);
    [Item]public static readonly Generation Over30  = new Generation(30, 39);
    [Item]public static readonly Generation Over20  = new Generation(20, 29);
    [Item]public static readonly Generation Under20 = new Generation(null, 19);
    private static ReadOnlyCollection<Generation> _items = CreateItems();

    private static ReadOnlyCollection<Generation> CreateItems()
    {
        var query =
            from field in typeof(Generation).GetFields(BindingFlags.Static | BindingFlags.Public)
            where (field.IsDefined(typeof(ItemAttribute), false))
            select (field.GetValue(null) as Generation);

        return new ReadOnlyCollection<Generation>(query.ToList());
    }

    public static Generation GetGeneration(int age)
    {
        return _items.Single(item => item.IsIncludeing(age));
    }


    private Generation(int? lowerLimit, int? upperLimit)
    {
        LowerLimit = lowerLimit;
        UpperLimit = upperLimit;
    }

    public readonly int? LowerLimit;
    public readonly int? UpperLimit;

    public bool IsIncludeing(int age)
    {
        bool isWithinLowerLimit = LowerLimit.HasValue ? (LowerLimit <= age) : true;
        bool isWithinUpperLimit = UpperLimit.HasValue ? (age <= UpperLimit) : true;
        return isWithinLowerLimit && isWithinUpperLimit;
    }
}


若干やりすぎた感があります。


2009/06/15 01:33
bleis-tift さんが前に言ってた、D 言語の強い typedef が C# にも欲しいなぁです。
まぁ無いものはしょうがないってことで、お遊びでこんなクラス作ってみました。

Int32Def(TSelf).cs
using System;
using System.Runtime.Serialization;
using System.Reflection;

public abstract class Int32Def<TSelf>
    where TSelf : Int32Def<TSelf>
{
    private readonly int _value;

    public static implicit operator int(Int32Def<TSelf> value)
    {
        return value._value;
    }

    public static explicit operator Int32Def<TSelf>(int value)
    {
        TSelf obj = (TSelf)FormatterServices.GetUninitializedObject(typeof(TSelf));
        FieldInfo valueField = typeof(Int32Def<TSelf>).GetField("_value", BindingFlags.NonPublic | BindingFlags.Instance);
        valueField.SetValue(obj, value);
        return obj;
    }

    public static TSelf operator +(Int32Def<TSelf> value)
    {
        return (TSelf)(+value._value);
    }

    public static TSelf operator -(Int32Def<TSelf> value)
    {
        return (TSelf)(-value._value);
    }

    public static TSelf operator ~(Int32Def<TSelf> value)
    {
        return (TSelf)(~value._value);
    }

    public static TSelf operator ++(Int32Def<TSelf> value)
    {
        int temp = value._value;
        temp++;
        return (TSelf)(temp);
    }

    public static TSelf operator --(Int32Def<TSelf> value)
    {
        int temp = value._value;
        temp--;
        return (TSelf)(temp);
    }

    public static TSelf operator +(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value + value2._value);
    }

    public static TSelf operator -(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value - value2._value);
    }

    public static TSelf operator *(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value * value2._value);
    }

    public static TSelf operator /(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value / value2._value);
    }

    public static TSelf operator %(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value % value2._value);
    }

    public static TSelf operator &(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value & value2._value);
    }

    public static TSelf operator |(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value | value2._value);
    }

    public static TSelf operator ^(Int32Def<TSelf> value1, Int32Def<TSelf> value2)
    {
        return (TSelf)(value1._value ^ value2._value);
    }

    public static TSelf operator <<(Int32Def<TSelf> value1, int value2)
    {
        return (TSelf)(value1._value << value2);
    }

    public static TSelf operator >>(Int32Def<TSelf> value1, int value2)
    {
        return (TSelf)(value1._value >> value2);
    }
}


Program.cs
using System;

class Hoge : Int32Def<Hoge>
{
}

class Fuga : Int32Def<Fuga>
{
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Test((Hoge)5, (Hoge)10)); //=> 15
        Console.WriteLine(Test((Fuga)5, (Fuga)10)); //=> 50
    }

    static Hoge Test(Hoge a, Hoge b)
    {
        return a + b;
    }

    static Fuga Test(Fuga a, Fuga b)
    {
        return a * b;
    }
}



あくまでもお遊びです。Int32 限定ですし、構造体ではなくクラス (従って null がありえますし現時点では対策してません) です。あと、演算子しか考慮してません。

ちなみに T4 Template 版もあります。まだ詰めが甘いですが。こちらは構造体としてコード生成するので、null を許容しません。あと、Int32 以外のプリミティブ型もたぶん行けます、たぶん。
こっちはもうちょい頑張れば実用的なものにできるかなと思います。

<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#
    string namespaceName = "Samples";
    string newTypeName = "Hoge";
    string sourceTypeName = "int";
   
    string[] unaryOperators = new string[]
    {
        "+",
        "-",
        "~",
        //"!",
    };
   
    string[] destructiveUnaryOperators = new string[]
    {
        "++",
        "--",
    };
   
    string[] binaryOperators = new string[]
    {
        "+",
        "-",
        "*",
        "/",
        "%",
        "&",
        "|",
        "^",
    };
   
    string[] booleanOperators = new string[]
    {
        "true",
        "false"
    };
   
    string[] shiftOperators = new string[]
    {
        "<<",
        ">>"
    };
   
    string[] comparisonOperators = new string[]
    {
        "==",
        "!=",
        "<",
        ">",
        "<=",
        ">="
    };
#>
namespace <#= namespaceName #>
{
    public struct <#= newTypeName #>
    {
        public <#= newTypeName #>(<#= sourceTypeName #> value)
        {
            this._value = value;
        }
       
        private readonly <#= sourceTypeName #> _value;
       
        public static implicit operator <#= sourceTypeName #>(<#= newTypeName #> value)
        {
            return value._value;
        }
       
        public static explicit operator <#= newTypeName #>(<#= sourceTypeName #> value)
        {
            return new <#= newTypeName #>(value);
        }
<#
    foreach (string unaryOperator in unaryOperators)
    {
#>

        public static <#= newTypeName #> operator <#= unaryOperator #>(<#= newTypeName #> value)
        {
            return new <#= newTypeName #>(<#= unaryOperator #>value._value);
        }
<#
    }
#>
<#
    foreach (string destructiveUnaryOperator in destructiveUnaryOperators)
    {
#>

        public static <#= newTypeName #> operator <#= destructiveUnaryOperator #>(<#= newTypeName #> value)
        {
            <#= sourceTypeName #> temp = value._value;
            temp<#= destructiveUnaryOperator #>;
            return new <#= newTypeName #>(temp);
        }
<#
    }
#>
<#
    foreach (string binaryOperator in binaryOperators)
    {
#>

        public static <#= newTypeName #> operator <#= binaryOperator #>(<#= newTypeName #> value1, <#= newTypeName #> value2)
        {
            return new <#= newTypeName #>(value1._value <#= binaryOperator #> value2._value);
        }
<#
    }
#>
<#
    foreach (string shiftOperator in shiftOperators)
    {
#>

        public static <#= newTypeName #> operator <#= shiftOperator #>(<#= newTypeName #> value1, int value2)
        {
            return new <#= newTypeName #>(value1._value <#= shiftOperator #> value2);
        }
<#
    }
#>
<#
/*
    foreach (string booleanOperator in booleanOperators)
    {
#>

        public static bool operator <#= booleanOperator #>(<#= newTypeName #> value)
        {
            throw new System.NotImplementedException();
        }
<#
    }
*/
#>
    }
}
2009/05/17 23:47
仮想メソッドは、それ自体が実装を持ち、派生クラスでオーバーライドすればその実装を完全に上書きすることが可能です。
C# だと、明示的に virtual をくっつけないと仮想メソッドにはなりません。(Java だとデフォルトが仮想メソッドだった気がします。)
で、個人的に仮想メソッドはほいほい用意すべきものではないと思っています。決して、仮想メソッドが全く必要無いということではないですが。


まず、実装を完全に上書きすることができるってのがどうにも気持ち悪いです。
これが実際に行われる時は、主に継承ツリーが不適切である場合が多いです。
この場合、例えば仮想メソッドを抽象メソッドにして派生クラスの一つに実装を移すことで回避できたりします。

あと、base キーワード使って、基本クラスが実装したバージョンのメソッドを好きな位置で呼び出すことができますが (でも正しく動作させるにはちゃんと位置を考えないといけないことがよくある)、これも気持ち悪いです。
これが実際に行われる時は、クラスの内部設計が未熟である場合が多いです。
この場合、例えばテンプレートメソッドパターン等の検討を行い、派生クラスで実装する処理を的確に表現するメソッドを用意することで回避できたりします。


ただし、仮想メソッドが適切である場面もあります。
例えば、テンプレートメソッドパターン等によって用意されたメソッドだけれど、派生クラスで必ずしも実装する必要がないという場合は、抽象メソッドではなく実装が空の仮想メソッドとした方が良い場合もあります。
また、継承していくにつれ段階的に実装が膨らんでいく (あくまでも同じ目的の処理が増えていく) 可能性があるメソッド (例 : Dispose メソッド) なんかは仮想メソッドの方が適切です。


ということで、一概に仮想メソッドはダメとは言えないのですが、仮想メソッドを用意する時は他にもっと適切な方法がないかを考慮してみると良いかなと思います。
2009/03/10 13:32
0 ~ 10 行が基本。
10 ~ 20 行となると、ちょっと長い。
20 ~ 30 行となると、長い。
30 ~ 40 行となると、かなり長い。
40 行以上はやばい。(※)


※ ごく稀に仕方のないものもある。例えば switch 文による何かしらの変換処理とか。
2009/01/29 18:30
フィールドごとに getter/setter を用意するな - 予定は未定Blog版
OOコード養成ギブス - rants


最近、パブリックプロパティ (もしくはパブリックフィールド) の善悪について悩んでます。
例えば、ドメインオブジェクトを UI に表示する処理って、ドメインオブジェクトに持たせるわけにはいかないですよね?
かといって、DI で UI 表示処理を注入するってのも現実的じゃないと思います。

で、ドメインオブジェクトにパブリックプロパティを用意するとこうなります。

public sealed class Book
{
    public Book(string id, string title)
    {
        this._id = id;
        this._title = title;
    }

    private readonly string _id;
    private readonly _title;

    public string Id { get { return this._id; } }
    public string Title { get { return this._title; } }
}

public sealed class BookDetail : Page
{
    protected void Page_PreRender(object sender, EventArgs e)
    {
        this._idTextBox.Text = this._book.Id; // Book インスタンスの持ってき方はてきとー
        this._titleTextBox.Text = this._book.Title;
    }
}

でもこれだと、後から Book に出版社名を追加した時、BookDetail の修正漏れが発生する可能性があります。
そこで、こんな風にしてみます。

public sealed class Book
{
    public Book(string id, string title)
    {
        this._id = id;
        this._title = title;
    }

    private readonly string _id;
    private readonly string _title;

    public void Present(Receiver receiver)
    {
        receiver(this._id, this._title);
    }

    public delegate void Receiver(string id, string title);
}

public sealed class BookDetail : Page
{
    protected void Page_PreRender(object sender, EventArgs e)
    {
        Book.Receiver receiver = delegate(string id, string title)
        {
            this._idTextBox.Text = id;
            this._titleTextBox.Text = title;
        };
        this._book.Present(receiver); // Book インスタンスの持ってき方はてきとー
    }
}

後から Book に出版社名を追加したら、Receiver デリゲートのシグネチャが変わるため、BookDetail を修正せずにコンパイルを通すことはできません。

でもホントにこっちの方がいいんだろーか。
タイトルだけを表示に使用する画面でも、(タイトルの getter はないので) Present メソッドを使用してタイトルを受け取る必要があります。
それでも良いような気もするし悪いような気もします。

あ、Identity (Book なら ID) に関しては、なんとゆーか独立した存在価値 (?) みたいなものを持っているので、どちらにせよパブリックプロパティを用意 (もしくはパブリックフィールド化) して良いと思います。


// 追記
Java で似たようなコード書いてみました。

public final class Book {
    public Book(String id, String title) {
        this.id = id;
        this.title = title;
    }

    private final String id;
    private final String title;

    public void present(Receiver receiver) {
        receiver.receive(this.id, this.title);
    }

    public interface Receiver {
        void receive(String id, String title);
    }

    public static void main(String[] args) {
        Book book = new Book("1234", "aiueo");
        Book.Receiver receiver = new Book.Receiver() {
            public void receive(String id, String title) {
                System.out.println(id);
                System.out.println(title);
            }
        };
        book.present(receiver);
    }
}


// 2009/02/02
Visitor より Receiver の方がいい感じなので変更。
2009/01/23 01:15
インスタンスが属するクラスをあとから変更する操作をいろいろな言語で - sumim’s smalltalking-tos

直交座標系とか極座標系とかはよくわかってないけど、とりあえず C# でもできます。
まぁ、2つのインスタンスのメモリ上のサイズが等しくない場合はマズいことになりかねませんけどね…。
Cartesian と Polar はサイズが等しいので大丈夫です。

// 追記1 (2009/01/28)
対象オブジェクトのフィールドを辿るとマネージヒープ上のオブジェクトへの参照が含まれている、という場合も、場合によってはマズいことになります。

// 追記2 (2009/01/28)
NyaRuRu さんより、「何が起きても不思議ではない」とご指摘頂きました。
今回のコードは動作しましたが、インスタンスサイズを揃えて、フィールドにマネージヒープ上のオブジェクトへの参照を含めないようにしたとしても、確実に大丈夫だと断言することはできません。また、今回のコードが如何なる時でも確実に動作すると断言することもできません。

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

class Cartesian
{
    public Cartesian(double x, double y)
    {
        this.X = x;
        this.Y = y;
    }

    public double X { get; set; }

    public double Y { get; set; }

    public Polar ToPolar()
    {
        return new Polar(Math.Sqrt(X * X + Y * Y), Math.Atan2(Y, X));
    }
}

class Polar
{
    public Polar(double r, double theta)
    {
        this.R = r;
        this.Theta = theta;
    }

    public double R { get; set; }

    public double Theta { get; set; }

    public Cartesian ToCartesian()
    {
        return new Cartesian(R * Math.Cos(Theta), R * Math.Sin(Theta));
    }
}

class Program
{
    static void Transmogrify(object a, object b)
    {
        Type aType = a.GetType();
        Type bType = b.GetType();

        BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        FieldInfo[] aFieldInfos = aType.GetFields(flags);
        FieldInfo[] bFieldInfos = bType.GetFields(flags);

        var aFields = aFieldInfos.Select(item => new { Key = item, Value = item.GetValue(a) }).ToList();
        var bFields = bFieldInfos.Select(item => new { Key = item, Value = item.GetValue(b) }).ToList();

        ConvertType(a, bType);
        ConvertType(b, aType);

        aFields.ForEach(item => item.Key.SetValue(b, item.Value));
        bFields.ForEach(item => item.Key.SetValue(a, item.Value));
    }

    static unsafe void ConvertType(object target, Type newType)
    {
        IntPtr newTypeHandle = newType.TypeHandle.Value;
        GCHandle targetGCHandle = GCHandle.Alloc(target, GCHandleType.Normal);
        try
        {
            void* entryPointer = (void*)GCHandle.ToIntPtr(targetGCHandle);
            void* targetPointer = *((void**)entryPointer);
            IntPtr* typeHandlePointer = (IntPtr*)targetPointer;
            *typeHandlePointer = newTypeHandle;
        }
        finally
        {
            targetGCHandle.Free();
        }
    }

    static void Main(string[] args)
    {
        Polar pos1 = new Polar(Math.Sqrt(2), Math.PI / 4);
        Polar pos2 = pos1;
        Console.WriteLine(pos1.GetType().Name);             //=> Polar

        Transmogrify(pos1, pos1.ToCartesian());
        Console.WriteLine(pos1.GetType().Name);             //=> Cartesian
        Cartesian pos1AsCart = pos1 as object as Cartesian;
        Console.WriteLine(pos1AsCart.X);                    //=> 1
        Console.WriteLine(pos1AsCart.Y);                    //=> 1
        Console.WriteLine(pos2.GetType().Name);             //=> Cartesian

        Transmogrify(pos1, pos1AsCart.ToPolar());
        Console.WriteLine(pos1.GetType().Name);             //=> Polar
        Console.WriteLine(pos1.R);                          //=> 1.4142135623731
        Console.WriteLine(pos1.Theta / Math.PI);            //=> 0.25

        Console.ReadLine();
    }
}


[関連]
C#と諸々 オブジェクトの型を破壊的に変換
C#と諸々 ガベージコレクションを開始するには
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。