20

おそらく、C# や .NET フレームワークのよく使用される機能について無知であることを示しているのかもしれませんが、EmailAddressどのエイリアスのような型エイリアスを作成するためにネイティブにサポートされている方法があるかどうかを知りたいのですが、stringそれを拡張することができますのような私自身の方法bool Validate()

私はusing x = Some.Type;エイリアスを知っていますが、これらはグローバルではなく、型の安全性も提供しません。つまりstring、現在のファイルでエイリアスを使用するために通常のエイリアスを交換できます。私はEmailAddress自分自身のタイプであり、独立していてstring、それが影を落としているタイプと交換できないことを望んでいます。

私の現在の解決策はpublic sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable、ボイラープレートの暗黙的な文字列変換演算子などを生成する T4 テンプレートを使用してクラスを生成することです。今のところこれで問題なく、多くの柔軟性が得られますが、心の奥底では、強力な型エイリアスを作成するのと同じくらい簡単なことを行うために、これほど大量の定型コードを生成しなければならないのはばかげているように思えます。

これはコード生成以外では不可能かもしれませんが、他の人が自分のデザインで同様のことを試みているかどうか、またあなたの経験がどうであったか興味があります. 少なくとも、これは C# の仮想的な将来のバージョンで、このようなエイリアス機能の適切なユースケースとして機能する可能性があります。ありがとう!

編集:これから私が望む本当の価値は、データのさまざまなタイプ/形式を表すプリミティブ型でタイプ セーフを取得できることです。たとえば、 anEmailAddressと aSocialSecurityNumberと aPhoneNumberはすべてstring、基になる型として使用されますが、それ自体は交換可能な型ではありません。これにより、あいまいさの少ないメソッドオーバーロードの可能性が増えるという追加の利点は言うまでもなく、はるかに読みやすく自己文書化されたコードが得られると思います。

4

7 に答える 7

5

.NET Framework を見ると、System.Uri が電子メール アドレスに最も近い例です。.NET では、パターンはクラスで何かをラップし、そのように制約を追加することです。

単純な型に追加の制約を追加する強力な型付けを追加することは、一部の関数型言語が持っていると私が信じている興味深い言語機能です。フィートなどの次元単位を値に追加し、方程式の次元分析を行って単位が一致していることを確認できる言語の名前を思い出せません。

于 2009-12-18T00:28:15.293 に答える
4

string封印されている理由の背景:

http://www.code-magazine.com/Article.aspx?quickid=0501091から:

ロリー:ちょっとジェイ、私があなたにいくつか質問してもいいですか?私はすでにいくつかのことに興味があります。まず第一に、これは私が今週行ったMSDNイベントの1つで取り上げられましたが、なぜStringが封印されているのですか?注:VB.NETプログラマーの場合、Sealed=NotInheritableです。

Jay:Stringで多くの魔法のトリックを実行して、比較などを最適化できるようにし、可能な限り高速にするためです。だから、私たちは物事をマークアップするためにそこにあるポインターや他のものからビットを盗んでいます。例を挙げると、私が始めたときはこれを知りませんでしたが、文字列にハイフンまたはアポストロフィが含まれている場合は、テキストが含まれている場合とは異なる方法で並べ替えられます。並べ替えのアルゴリズムハイフンまたはアポストロフィがある場合、グローバル対応の並べ替えを行う場合はかなり複雑なので、文字列にそのタイプの動作があるかどうかを実際にマークします。

ロリー:つまり、ストリングスの世界では、ストリングスを封印しなかった場合、人々がそれをサブクラス化しようとすると、多くの大混乱を引き起こす余地がたくさんあるということです。

ジェイ:その通りです。オブジェクトのレイアウト全体が変更されるため、ピックアップ速度でプレイするトリックをプレイすることはできません。

これはおそらく以前に見たことがあるCodeProjectの記事です。

http://www.codeproject.com/KB/cs/expandSealed.aspx

そうですね、暗黙の演算子が唯一の解決策です。

于 2009-12-18T00:05:03.070 に答える
2

クラスはあなたSystem.Net.Mail.MailAddressのニーズに合っていますか、それとも少なくとも「助け」ですか?

編集:明示的にIEquatableまたはISerializableではありませんが、独自のラッパーにそれらを簡単に追加できます。

于 2009-12-17T23:51:01.217 に答える
2

あなたは少なくとも妥当な C# 知識を持っているように見えるので、私の答えはばかげているように見えるかもしれませんが、あなたが望むのは「型階層」と呼ばれ、String クラスをコーディングした人たちは、この「OO 機能」を使用できないようにしたかったため、String を作成しました。クラスが封印されているため、やりたいことができなくなります。最善のアプローチは、現在取り組んでいる方法です。独自の型を作成し、文字列への暗黙的な変換を行います。

于 2009-12-18T00:29:11.393 に答える
1

強い型と暗黙的な文字列変換の両方を同時に実行したい理由がわからないと思います。私にとって、一方が他方を除外します。

私はintについて同じ問題を解決しようとしました(タイトルにintが記載されていますが、質問には記載されていません)。列挙型を宣言すると、intから/に明示的にキャストする必要があるタイプセーフな整数が得られることがわかりました。

アップデート

列挙型は開集合を対象としていない場合がありますが、それでもそのような方法で使用できます。このサンプルは、データベース内のいくつかのテーブルのID列を区別するためのコンパイル実験からのものです。

    enum ProcID { Unassigned = 0 }
    enum TenderID { Unassigned = 0 }

    void Test()
    {
        ProcID p = 0;
        TenderID t = 0; <-- 0 is assignable to every enum
        p = (ProcID)3;  <-- need to explicitly convert

        if (p == t)  <-- operator == cannot be applied
            t = -1;  <-- cannot implicitly convert

        DoProc(p);
        DoProc(t);   <-- no overloaded method found
        DoTender(t);
    }

    void DoProc(ProcID p)
    {
    }

    void DoTender(TenderID t)
    {
    }
于 2009-12-18T00:15:00.420 に答える
1

拡張メソッドを使いたいと思います。これらを使用すると、新しい派生型を作成せずにクラス機能を拡張できます。

于 2009-12-18T00:19:30.037 に答える
0

同じニーズをカバーするためにこのクラスを作成しました。これは「int」型用です(「string」用もあります):

public class NamedInt : IComparable<int>, IEquatable<int>
{
    internal int Value { get; }

    protected NamedInt() { }
    protected NamedInt(int val) { Value = val; }
    protected NamedInt(string val) { Value = Convert.ToInt32(val); }

    public static implicit operator int (NamedInt val) { return val.Value; }

    public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
    public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
    public static bool operator !=(NamedInt a, int b) { return !(a==b); }
    public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }

    public bool Equals(int other) { return Equals(new NamedInt(other)); }
    public override bool Equals(object other) {
        if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
        return Equals(new NamedInt(other.ToString()));
    }
    private bool Equals(NamedInt other) {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(Value, other.Value);
    }

    public int CompareTo(int other) { return Value - other; }
    public int CompareTo(NamedInt other) { return Value - other.Value; }

    public override int GetHashCode() { return Value.GetHashCode(); }

    public override string ToString() { return Value.ToString(); }
}

そして、あなたの場合にそれを消費するには:

public class MyStronglyTypedInt: NamedInt {
    public MyStronglyTypedInt(int value) : base(value) {
        // Your validation can go here
    }
    public static implicit operator MyStronglyTypedInt(int value) { 
        return new MyStronglyTypedInt(value);
    }

    public bool Validate() {
        // Your validation can go here
    }
}

シリアル化できるようにする必要がある場合 (Newtonsoft.Json)、お知らせください。コードを追加します。

于 2015-12-08T00:19:13.073 に答える