5

次のクラスを想像してください。

public class Settings
{
    [FileBackedProperty("foo.txt")]
    public string Foo { get; set; }
}

上記に似たものを書きsettings.Foo、ファイル「foo.txt」から読み取り、「foo.txt」に書き込めるようにしたいと思いますsettings.Foo = "bar"

明らかにこれは単純化された例であり、本番アプリケーションでは上記のことを行いませんが、Foo を ASP.net に格納したい場合など、他の例があります。何度もコード:

public int Foo
{
    get
    {
        if (Session["foo"] != null)
            return Convert.ToInt32(Session["foo"]);
        else
            // Throw an exception or return a default value
    }
    set
    {
        Session["foo"] = value;
    }
}

(繰り返しになりますが、この例は単純化されており、上記のコードは書きません。実際には嘘をついています。上記のコードがあり、リファクタリングに取り組んでいるため、この質問です)

上記の例は、すべてが同様のロジックを持つ 50 の異なるセッション値がない限り問題ありません。それで、2番目のプロパティを最初のプロパティに似たものに変換できる方法はありますか? (属性とリフレクションを使用するか、または他の方法を使用しますか?)

4

6 に答える 6

6

これがあなた (そして私) が必要としていたものではないことはわかっています。これは、サードパーティのライブラリを使用しない場合に最も近いものです。get&set メソッドのロジックを変更し、GetProperty および GetCustomAttributes メソッドのキャッシュを追加できます。または、基底クラスが既にある場合は、get&set メソッドをヘルパー クラスで static として記述できます。繰り返しますが、完璧な答えではなく、パフォーマンスが低下する可能性もありますが、少なくともコピーして貼り付けるコードが減少します (:

注: コンパイラがプロパティをインライン化するのを防ぐために、プロパティを仮想にすることが重要です。

public class SampleClass : SessionObject
{
    [Session(Key = "SS_PROP")]
    public virtual int SampleProperty
    {
        get { return get(); }
        set { set(value); }
    }

    [Session(Key = "SS_PROP2")]
    public virtual string SampleProperty2
    {
        get { return get(); }
        set { set(value); }
    }
}

[AttributeUsage(AttributeTargets.Property)]
public class SessionAttribute : Attribute
{
    public string Key { get; set; }
}

public abstract class SessionObject
{
    Dictionary<string, object> Session = new Dictionary<string, object>();

    protected void set(object value)
    {
        StackFrame caller = new StackFrame(1);
        MethodBase method = caller.GetMethod();
        string propName = method.Name.Substring(4);
        Type type = method.ReflectedType;
        PropertyInfo pi = type.GetProperty(propName);
        object[] attributes = pi.GetCustomAttributes(typeof(SessionAttribute), true);
        if (attributes != null && attributes.Length == 1)
        {
            SessionAttribute ssAttr = attributes[0] as SessionAttribute;
            Session[ssAttr.Key] = value;
        }
    }

    protected dynamic get()
    {
        StackFrame caller = new StackFrame(1);
        MethodBase method = caller.GetMethod();
        string propName = method.Name.Substring(4);
        Type type = method.ReflectedType;
        PropertyInfo pi = type.GetProperty(propName);
        object[] attributes = pi.GetCustomAttributes(typeof(SessionAttribute), true);
        if (attributes != null && attributes.Length == 1)
        {
            SessionAttribute ssAttr = attributes[0] as SessionAttribute;
            if (Session.ContainsKey(ssAttr.Key))
            {
                return Session[ssAttr.Key];
            }
        }
        return default(dynamic);
    }
}
于 2012-07-23T08:23:44.210 に答える
3

別のオプションは、PostSharpの使用です。属性を定義するとIL最終的なコードが挿入されるため、ソース コードは変更されません。悪い面と良い面があります。

この商品は無料ではありません。

いくつかの開始のヒント。

お役に立てれば。

于 2012-04-04T20:41:30.280 に答える
1

あなたがやろうとしていることは「アスペクト指向プログラミング」と呼ばれ、必要なコードが非常に小さな変更だけで何度も複製される特定のタスクでは比較的一般的です。あなたの例は確かに資格があります。

基本的な考え方は次のとおりです。クラスまたはクラス メンバーを装飾するために使用できる属性を作成します。この属性は、呼び出されたときに実行されるメソッドにメソッド インターセプターをフックできるようにする CLR のメッセージ パッシング システムの "コンテキスト" を定義します。

パフォーマンスに重大な影響があることを理解してください。属性によって装飾されたメンバーを持つオブジェクトは、MarshallByRefObject または ContextBoundObject から継承する必要があります。これらのいずれかは、実際に属性の装飾を行わなくても、実行時のオブジェクトのパフォーマンスに約 10 倍の影響を与えます。

コード例を次に示します: http://www.developerfusion.com/article/5307/aspect-directional-programming-using-net/3/

また、動的プロキシを使用して、属性の装飾やその他の反射ベースの情報に基づいてオブジェクトを「その場で」作成することもできます。これは、ORM や IoC フレームワークなど、C# 開発者が当然と思っている多くのものの背後にあるテクノロジです。基本的には、Castle DynamicProxy のようなものを使用して、ベース オブジェクトのように見えるオブジェクトを作成しますが、プロパティの定義はオーバーライドされます。ファイルベースの作成/永続化ロジックを持つ属性で装飾されています。

于 2012-04-04T20:39:10.157 に答える
1

ゲッター コードをあまり記述したくない場合は、ヘルパー メソッドを記述します。

public int Foo
{
    get
    {
        return GetHelper<int>("foo");
    }
    set
    {
        Session["foo"] = value;
    }
}

public T GetHelper<T>(string name, T defaultValue = default(T))
{
    if (Session[name] != null)
        return (T)Session[name];
    else
    {
        return defaultValue;
    }
}

ダイナミクスにアクセスできる場合は、動的オブジェクトを使用してセッションをラップできます。

internal class DynamicSession : DynamicObject
{
    private HttpSessionState_session;

    public DynamicSession()
    {
        _session = HttpContext.Current.Session;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_session[binder.Name] != null)
        {
            result = _session[binder.Name];
            return true;
        }
        result = null;
        return false;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        _session[binder.Name] = value;
        return true;
    }
}

そして、次のように使用できます。

dynamic session = new DynamicSession();
    //These properties are "magically" put in and taken out of session!
//get
int foo = session.Foo;
//set
session.Foo = 3;

最後のオプションは、Resharper のLive Templatesのようなもので、コードの入力をより簡単にします。

于 2012-04-04T20:34:23.457 に答える
1

実行時にこれを行う場合は、Dr. Dobbs の次の記事「Generating Code at Run Time With Reflection.Emit 」を参照してください。

于 2012-04-04T20:36:34.697 に答える