通过反射或类似方式动态生成属性getter/setter

the*_*sdj 5 c# reflection metaprogramming

想象一下以下课程:

public class Settings
{
    [FileBackedProperty("foo.txt")]
    public string Foo { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够编写类似于上面的内容,并settings.Foo从文件"foo.txt"中读取并settings.Foo = "bar"写入"foo.txt".

显然这是一个简化的例子,我不会在生产应用程序中执行上述操作,但还有其他示例,例如,如果我希望Foo存储在ASP.net会话状态"foo"中,但我厌倦了编写以下内容代码一遍又一遍:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

(再一次这个例子是简化的,我不会写上面的代码,实际上我在撒谎,我有上面的代码,我正在努力重构它,因此这个问题)

除非你有50个不同的会话值都具有相似的逻辑,否则上面的例子很好.那么我有可能将第二个属性转换为类似于第一个属性的东西吗?(使用属性和反射,或者其他一些方法?)

Meh*_*taş 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


vcs*_*nes 1

如果您想避免编写太多 getter 代码,请编写一个辅助方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您有权访问动态,那么您可以使用动态对象来包装会话:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

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

最后一个选项是Resharper 中的实时模板,可以使代码输入变得更加容易。