3

次のクラス階層があります。

public abstract class ResourceBase { }

public abstract class WebResourceBase : ResourceBase  {
  public ResourceBase LocalPath { get; set; }
  public ResourceBase FtpPath { get; set; }
}

public class JavaScript : WebResourceBase { }

私がやりたいのは、そのような宣言をすることです。

new JavaScript() {
  LocalPath = "/path/goes/here/1.js",
  FtpPath = "ftp://path/goes/here/1.js"
}

LocalPathここでの明白な答えは、暗黙の演算子を使用することですが、問題は、宣言された型と同じであり、型になるこれらのプロパティに派生型を割り当てたいことFtpPathですJavaScript

ソリューションを現在のソリューションよりも柔軟にしたいと考えています。このコードは私の肌をクロールさせます。StackTraceリフレクションを使用する方法があることを望んでいたので、クラスを使用して情報を探してみましたが、うまくいきませんでした。どんな助けでも大歓迎です。ありがとう。

public abstract class ResourceBase { 
  public static implicit operator ResourceBase(string path) {
            if (path.EndsWith(".js"))
                return new JavaScript(path);
            // etc...
  }
}
4

4 に答える 4

3

WebResourceBaseこれは、が実際に継承されることを前提としていResourceBaseます。

残念ながら、暗黙的な演算子の見栄えを良くすることはできません-ジェネリックはここでは機能しません。


代替手段: ResourceBase を制約するジェネリック

私はそれを読み直し、あなたが何を求めているのかを理解したので、1つのオプションは、クラスを修正して、派生クラスを参照するジェネリックパラメーターを含めることです(自己参照のようなものです):

public abstract class ResourceBase 
{ }

public abstract class WebResourceBase<T> : ResourceBase
    where T : WebResourceBase<T>
{
    public T LocalPath { get; set; }
    public T FtpPath { get; set; }
}

public class JavaScript : WebResourceBase<JavaScript> 
{ 
}

JavaScript次に、プロパティLocalPathともFtpPathタイプになっていることがわかりますJavaScript

これで、割り当ては次のタイプのみを受け入れJavaScriptます。

new JavaScript() 
{
    LocalPath = new JavaScript("/path/goes/here/1.js"),
    FtpPath = new JavaScript("ftp://path/goes/here/1.js")
}

このアプローチの利点は、基本プロパティが現在の型またはより派生したものになるように制約することです。


implicit代替手段:演算子の代わりに明示的な解析を行う

LocalPathおよびFtpPath変数を のままにしておく必要がある場合ResourceBase、またはここでジェネリックを使用できない場合、暗黙の演算子が混乱し始めます。静的メソッドのような明示的なものを提供することをお勧めします:

new JavaScript() 
{
    LocalPath = JavaScript.Parse("/path/goes/here/1.js"),
    FtpPath = JavaScript.Parse("ftp://path/goes/here/1.js")
}

class JavaScript
{
    public static ResourceBase Parse(string s)
    {
        if (path.EndsWith(".js"))
            return new JavaScript(path);

        throw new Exception();
    }
}


implicit代替手段:演算子の代わりにクラス階層を解析する

コンストラクターを介して文字列を型に取り込むという概念を焼き付け、プロパティを読み取り専用でパブリックにします。

public abstract class ResourceBase
{ }

public abstract class WebResourceBase
{
    public ResourceBase LocalPath { get; private set; }
    public ResourceBase FtpPath { get; private set; }

    protected abstract ResourceBase ParseLocalPath(string s);
    protected abstract ResourceBase ParseFtpPath(string s);
}

public class JavaScript : WebResourceBase<JavaScript> 
{ 
    protected override ResourceBase ParseLocalPath(string s)
    {
        // etc.
    } 
    protected override ResourceBase ParseFtpPath(string s)
    {
        // etc.
    } 
}


正直なところ、これのほとんどは、文字列から特定の型として設定された 2 つのプロパティを取得するだけでは少しやり過ぎに思えます。多くのオプションがあります。暗黙の演算子でさえ機能します。

一番わかりやすいものを選んでください。オペレーターのオーバーロードは、掘り下げない限り、やや隠されている傾向があります。

于 2012-07-12T13:32:51.207 に答える
1

私もそれWebResourceBaseがから継承することになっていると思いますResourceBase

protected派生クラスが自分自身をサブスクライブできる基本クラスにマッピング メカニズムを用意します。

public abstract class ResourceBase
{
    // Records how to make a ResourceBase from a string, 
    // on a per-extension basis
    private static Dictionary<string, Func<string, ResourceBase>> constructorMap
        = new Dictionary<string, Func<string, ResourceBase>>();

    // Allows a derived type to subscribe itself
    protected static void Subscribe(
        string extension, 
        Func<string, ResourceBase> ctor)
    {
        if (constructorMap.ContainsKey(extension))
            throw new Exception("nuh uh");

        constructorMap.Add(extension, ctor);
    }

    // Given a string, finds out who has signed up to deal with it,
    // and has them deal with it
    public static implicit operator ResourceBase(string s)
    {
        // Find a matching extension
        var matches = constructorMap.Where(kvp => s.EndsWith(kvp.Key)).ToList();

        switch (matches.Count)
        {
            case 0:
                throw new Exception(
                  string.Format("Don't know how to make {0} into a ResourceBase",
                                s));
            case 1:
                return matches.Single().Value(s);
            default:
                throw new Exception(string.Format(
                  "More than one possibility for making {0} into a ResourceBase",
                  s));
        }
    }
}

中間型はほとんど変更されていませんが、コンパイル時に強制する方法がわからないいくつかの型チェックがあります。

public abstract class WebResourceBase : ResourceBase
{
    private ResourceBase localPath;
    public ResourceBase LocalPath
    {
        get { return localPath; }
        set
        {
            if (value.GetType() != GetType())
            {
                throw new Exception("Naughty");
            }
            localPath = value;
        }
    }        

    private ResourceBase ftpPath;
    public ResourceBase FtpPath
    {
        get { return ftpPath; }
        set
        {
            if (value.GetType() != GetType())
            {
                throw new Exception("Naughty");
            }
            ftpPath = value;
        }
    }
}

具体的な型は次のようになります。

public class JavaScript : WebResourceBase
{
    public JavaScript()
    {

    }
    private JavaScript(string s)
    {
    }

    static JavaScript()
    {
        Subscribe("js", s => (ResourceBase)new JavaScript(s));
    }
}

使用法はあなたが指定したとおりです:

        var js = new JavaScript
        {
            LocalPath = "hello.js",
            FtpPath = "hello.js"
        };

andResourceBaseの署名にあるにもかかわらず、上記のステートメントの後にandオブジェクトであることに注意してください。constructorMapSubscribeLocalPathFtpPath JavaScript

于 2012-07-12T14:13:44.747 に答える
0

これが私の考えです:

public abstract class WebResourceBase {
  public ResourceBase LocalPath { get; set; }
  public ResourceBase FtpPath { get; set; }

  protected abstract ResourceBase ConvertFromString(string path);
  public string LocalPathStr { set { LocalPath = ConvertFromString(value); } }
  public string FtpPathStr { set { FtpPath = ConvertFromString(value); } }
}

おそらく改善される可能性があります。

于 2012-07-12T13:50:48.560 に答える
0

プロセスに間接的なレイヤーを追加するだけです(設計に関する質問に対する適切な回答であることが多いのは驚くべきことです;))。

2 つの文字列パラメーターを受け取る新しいコンストラクターを追加するか、2 つの文字列プロパティを追加するか、またはその両方を行います。(ここからは、2 つの新しい文字列プロパティを追加するだけであると仮定します。そこから推測できます。)

新しいプロパティを追加する場合:その set メソッドで、文字列を固有JavaScriptLocalPathの派生型に変換し、その結果を使用してプロパティを設定できます。get メソッドは、その文字列を から抽出することもできると思います (そのため、わざわざ を保存する必要もありません)。 ResourceBaseJavaScriptLocationPathResourceBasestring

于 2012-07-12T13:45:30.840 に答える