17

状況

多くの異なる種類のファイル パスを内部的に処理するクラスがあります。ローカル、リモートなどです。相対的なものもあれば、絶対的なものもあります。

以前は、そのメソッドの多くがそれらをstrings として相互に渡していましたが、各メソッドが予期しているパスのタイプを正確に追跡することは非常に困難になりました。

希望の修正

したがって、本質的には、 、、、およびの 4つtypedefの異なるタイプが必要でした。このように、静的型チェッカーは、開発者がs を提供し、正しいセマンティクスを期待していることを確認するのに役立ちます。stringRemoteRelativeLocalRelativeRemoteAbsoluteLocalAbsolutestring

残念ながら、stringsealedBCL にあるため、単純な継承ではこれを行うことができませんでした。また、単純なtypedefがないため、そのようにすることもできませんでした。

実際の修正

最終的に、それぞれが を含む 4 つの異なる単純なクラスを作成しましたreadonly string

public struct LocalAbsolutePath {
    public readonly string path;
    public LocalAbsolutePath(string path) {
        this.path = path;
    }
}

これはほとんどの場合機能しますが、望ましくない冗長性が少し追加されてしまいます。

質問: 単純な C# 構文に自然に適合する代替案を見落としていませんか?

上で述べたように、C スタイルtypedef string LocalAbsolutePath;または F# スタイルでさえ、type LocalAbsolutePath = stringここでの私の夢です。しかし、カスタムクラスからの方向性の一歩であっても素晴らしいでしょう.

4

5 に答える 5

15

あなたの解決策は良いです。に型変換を追加することで、追加の冗長性と戦うことができstringます。LocalAbsolutePathstring

public struct LocalAbsolutePath { // Making it a class would be OK too
    private readonly string path; // <<=== It is now private
    public LocalAbsolutePath(string path) {
        this.path = path;
    }
    public static implicit operator string(LocalAbsolutePath p) {
        return p.path;
    }
}
于 2012-04-27T19:29:59.613 に答える
5

あなたがすべきことは、パスタイプに対して4つの異なるクラスを作成するという現在のアプローチを維持することです(そしてそれらに同じベースクラスを継承させることさえ)、それらの4つのPathオブジェクトのうちの1つだけを受け取るようにメソッドを制限できます。

var myPath = new LocalAbsolutePath("path")それほど冗長だとは思いませんvar myPath = "path"が、簡潔さに欠けているため、明示的に補うことができますが、本当に必要な場合は、クラスと文字列の間に暗黙のキャスト演算子を実装して、これを使用できます。仕事:

 public static implicit operator LocalAbsolutePath(string path)
 {
     return new LocalAbsolutePath(path);
 }

そして今、あなたはただすることができます:

LocalAbsolutePath myPath = "Path String";
于 2012-04-27T19:31:30.160 に答える
1

C# クラスでのような動作を提供するLikeTypeというNuGet パッケージを作成しました。typedef

これはあなたがそれを使用する方法です:

class CustomerId : LikeType<string>
{
    public CustomerId(string id) : base(id) { }
}

型の動作は次のとおりです。

void ShowTypeBehavior()
{
    var customerId = new CustomerId("cust-001"); // create instance with given backing value
    string custIdValue = customerId; // implicit cast from class to backing type, sets 'custIdValue' to "cust-001"

    var otherCustomerId = new CustomerId("cust-002");
    var areEqual = customerId == otherCustomerId; // false
    var areNotEqual = customerId != otherCustomerId; // true
    var areEqualUsingMethod = customerId.Equals(otherCustomerId); // false

    var customerIdCopy = new CustomerId("cust-001"); // create separate instance with same backing value
    var isCopyEqual = customerId == customerIdCopy; // true. Instances are considered equal if their backing values are equal.
}
于 2015-11-24T01:04:24.620 に答える
0

私が取り組んでいるプロジェクトで同じ目的に着手し、ここでの回答から大きな恩恵を受けたので、最終的に得た解決策を共有したいと思いました. 特に単体テストのアサートで null を処理すると、私は気が狂いそうになりました。以下はもちろん失敗します。

string someStringVar = null;
MyStringType myStringType = new MyStringType(someStringVar);
MyStringType myStringTypeNull = null;
Assert.AreEqual(myStringType, myStringTypeNull);

パブリック コンストラクターの代わりに静的な Parse() を使用すると、null を返すことができるため、より満足のいく結果が得られました。これは合格します:

string someStringVar = null;
MyStringType myStringType = MyStringType.Parse(someStringVar);
MyStringType myStringTypeNull = null;
Assert.AreEqual(myStringType, myStringTypeNull);

また、文字列から MyStringType への暗黙的な変換は望んでいませんでした。これは、そもそもこれを行う意識的なコーダーの利点の一部を取り除くように思えました。文字列からの暗黙的な変換を許可すると、MyStringType パラメーターを使用したメソッドの呼び出しが文字列を受け入れることになりますが、これは望ましくありませんでした。多くの文字列パラメーターを持つメソッドはエラーが発生しやすいためです。暗黙的に行われた逆変換は、私にとってより意味がありました。

最後に、これは簡単に再利用できるジェネリックの理想的なケースだと思いました。

とにかく、これが私が最終的に得たものです:

public class StringType<T> where T:class
{
    private readonly string _str;

    protected StringType(string str)
    {
        _str = str;
    }

    public static implicit operator string(StringType<T> obj)
    {
        return obj == null ? null : obj._str;
    }

    public override string ToString()
    {
        return _str;
    }

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


public class MyStringType : StringType<MyStringType>
{
    protected MyStringType(string str) : base(str) { }

    public static MyStringType Parse(object obj)
    {
        var str = obj is string ? (string)obj : (obj == null ? null : obj.ToString());
        return str == null ? null : new MyStringType(str);
    }
}

コメント/改善/単純化はもちろん大歓迎です!

于 2013-05-09T14:03:43.227 に答える
-2

たぶん私はここで間違ったツリーを吠えていますが、typedef私が知る限り、C#では型エイリアスとして実装されています。タイプエイリアスの設定は次のように簡単です。

using LocalAbsolutePath = System.String;

LocalAbsolutePathその後、有効なタイプとして使用を開始できます。多かれ少なかれこのように:

LocalAbsolutePath thisPath = "c:\\thisPath";

あなたの投稿に基づいて、私はこれがあなたが探していたものだと感じています。私が正しいことを願っています...!

于 2012-04-27T19:48:37.727 に答える