有没有办法避免C#中的属性内联优化?

有没有办法避免C#中的属性内联优化?,c#,stack-trace,C#,Stack Trace,因此,我有一个PropertyBag类,用于实现INotifyPropertyChanged。为了使此代码尽可能干净地工作并避免用户错误,我使用堆栈获取属性名。看,如果属性名与实际属性不完全匹配,那么您将失败,我正试图对此进行保护 下面是该类的一个示例用法: public class MyData : PropertyBag { public MyData() { Foo = -1; } public int Foo {

因此,我有一个PropertyBag类,用于实现INotifyPropertyChanged。为了使此代码尽可能干净地工作并避免用户错误,我使用堆栈获取属性名。看,如果属性名与实际属性不完全匹配,那么您将失败,我正试图对此进行保护

下面是该类的一个示例用法:

public class MyData : PropertyBag
{
    public MyData()
    {
        Foo = -1;
    }

    public int Foo
    {
        get { return GetProperty<int>(); }
        set { SetProperty(value); }
    }
}
公共类MyData:PropertyBag
{
公共MyData()
{
Foo=-1;
}
公共int-Foo
{
获取{return GetProperty();}
集合{SetProperty(value);}
}
}
基本PropertyBag的重要代码如下:

public abstract class PropertyBag : INotifyPropertyChanged
{
    protected T GetProperty<T>()
    {
        string propertyName = PropertyName((new StackTrace()).GetFrame(1));
        if (propertyName == null)
            throw new ArgumentException("GetProperty must be called from a property");

        return GetValue<T>(propertyName);
    }

    protected void SetProperty<T>(T value)
    {
        string propertyName = PropertyName((new StackTrace()).GetFrame(1));
        if (propertyName == null)
            throw new ArgumentException("SetProperty must be called from a property");

        SetValue(propertyName, value);
    }

    private static string PropertyName(StackFrame frame)
    {
        if (frame == null) return null;
        if (!frame.GetMethod().Name.StartsWith("get_") &&
           !frame.GetMethod().Name.StartsWith("set_"))
            return null;

        return frame.GetMethod().Name.Substring(4);
    }
}
公共抽象类PropertyBag:INotifyPropertyChanged
{
受保护的GetProperty()
{
字符串propertyName=propertyName((new StackTrace()).GetFrame(1));
如果(propertyName==null)
抛出新ArgumentException(“必须从属性调用GetProperty”);
返回GetValue(propertyName);
}
受保护的void SetProperty(T值)
{
字符串propertyName=propertyName((new StackTrace()).GetFrame(1));
如果(propertyName==null)
抛出新ArgumentException(“必须从属性调用SetProperty”);
SetValue(propertyName,value);
}
私有静态字符串PropertyName(StackFrame)
{
if(frame==null)返回null;
如果(!frame.GetMethod().Name.StartsWith(“get_”)&&
!frame.GetMethod().Name.StartsWith(“set_”))
返回null;
返回frame.GetMethod().Name.Substring(4);
}
}
现在你已经看到了我的代码,我可以告诉你这个问题。。。在发布版本的某些情况下,“MyData”构造函数中的“Foo”setter似乎正在优化为内联为SetProperty(-1)。不幸的是,这个内联优化失败了我的SetProperty方法,因为我不再从属性调用它了!失败。看来我不能以这种方式依赖StackTrace

谁能 答:找到一种更好的方法,但仍然避免将“Foo”传递给GetProperty和SetProperty?

B:想办法告诉编译器在这种情况下不要进行优化?

在这里使用堆栈速度慢而且没有必要;我只想使用:

get { return GetProperty<int>("Foo"); }
set { SetProperty("Foo", value); }
当然,您可以为键声明一个类型(使用
名称
属性),并使用该类型:

static readonly PropertyKey FooKey = new PropertyKey("Foo");
等等,;但是,要回答这个问题,请用以下标记(但不要这样做):


如果希望避免硬编码字符串,可以使用:

protected T GetProperty<T>(MethodBase getMethod)
{
    if (!getMethod.Name.StartsWith("get_")
    {
        throw new ArgumentException(
            "GetProperty must be called from a property");
    }
    return GetValue<T>(getMethod.Name.Substring(4));
}
protectedt GetProperty(MethodBase getMethod)
{
如果(!getMethod.Name.StartsWith(“get_”)
{
抛出新的ArgumentException(
“必须从属性调用GetProperty”);
}
返回GetValue(getMethod.Name.Substring(4));
}
根据需要添加更多的健全性检查

然后,该属性变为

public int Foo
{
    get { return GetProperty<int>(MethodInfo.GetCurrentMethod()); }     
}
public int-Foo
{
get{return GetProperty(MethodInfo.GetCurrentMethod());}
}
设置以相同的方式进行更改

GetCurrentMethod()也会遍历堆栈,但它是通过依赖于堆栈标记的(内部)非托管调用来完成的,因此也将在发布模式下工作

或者,对于带有MethodImplAttributes.NoOptimization)的快速修复[MethodImpl]或MethodImplAttributes.NoInLine也可以工作,尽管性能受到了影响(尽管考虑到每次都要遍历堆栈帧,但这种影响可以忽略不计)

另一种技术是获得某种级别的编译时检查:

public class PropertyHelper<T>
{
    public PropertyInfo GetPropertyValue<TValue>(
        Expression<Func<T, TValue>> selector)
    {
        Expression body = selector;
        if (body is LambdaExpression)
        {
            body = ((LambdaExpression)body).Body;
        }
        switch (body.NodeType)
        {
            case ExpressionType.MemberAccess:
                return GetValue<TValue>(
                    ((PropertyInfo)((MemberExpression)body).Member).Name);
            default:
                throw new InvalidOperationException();
        }
    }
}

private static readonly PropertyHelper<Xxxx> propertyHelper 
    = new PropertyHelper<Xxxx>();

public int Foo
{
    get { return propertyHelper.GetPropertyValue(x => x.Foo); }     
}
公共类属性帮助器
{
公共属性信息GetPropertyValue(
表达式选择器)
{
表达式体=选择器;
if(主体为LambdaExpression)
{
body=((LambdaExpression)body.body;
}
开关(body.NodeType)
{
case ExpressionType.MemberAccess:
返回GetValue(
((PropertyInfo)((MemberExpression)body.Member).Name);
违约:
抛出新的InvalidOperationException();
}
}
}
私有静态只读属性帮助器属性帮助器
=新属性Helper();
公共int-Foo
{
获取{return propertyHelper.GetPropertyValue(x=>x.Foo);}
}
其中Xxxxx是定义属性的类。如果静态特性导致问题(线程化或以其他方式使其成为实例值也是可能的)


我应该指出,这些技术实际上是出于兴趣,我并不是说它们是好的通用技术。

使用堆栈不是一个好主意。您依靠编译器的内部实现将属性包人工绑定到语言属性

  • 如果需要添加
    MethodImpl
    属性,那么对其他开发人员来说,使用属性包是不透明的
  • 即使属性包具有
    MethodImpl
    属性,也不能保证它将是调用堆栈上的第一个帧。程序集可能被检测或修改为在实际属性和对属性包的调用之间插入调用。(想想方面编程)
  • 新的语言甚至是未来版本的C编译器可能会以不同的方式装饰属性访问器,而不是
    “u get”和
    “u set”
  • 构造调用堆栈的操作相对较慢,因为它需要解压缩内部压缩堆栈,并使用反射获取每个类型和方法的名称

  • 实际上,您应该只实现属性包访问器,以获取一个参数来标识属性-字符串名称(如Hastable)或对象(如WPF依赖属性包)

    您可以尝试创建一个T4模板文件,以便为GetPr自动生成具有正确属性名称的属性
    [MethodImpl(MethodImplAttributes.NoOptimization
        | MethodImplAttributes.NoInlining)]
    
    protected T GetProperty<T>(MethodBase getMethod)
    {
        if (!getMethod.Name.StartsWith("get_")
        {
            throw new ArgumentException(
                "GetProperty must be called from a property");
        }
        return GetValue<T>(getMethod.Name.Substring(4));
    }
    
    public int Foo
    {
        get { return GetProperty<int>(MethodInfo.GetCurrentMethod()); }     
    }
    
    public class PropertyHelper<T>
    {
        public PropertyInfo GetPropertyValue<TValue>(
            Expression<Func<T, TValue>> selector)
        {
            Expression body = selector;
            if (body is LambdaExpression)
            {
                body = ((LambdaExpression)body).Body;
            }
            switch (body.NodeType)
            {
                case ExpressionType.MemberAccess:
                    return GetValue<TValue>(
                        ((PropertyInfo)((MemberExpression)body).Member).Name);
                default:
                    throw new InvalidOperationException();
            }
        }
    }
    
    private static readonly PropertyHelper<Xxxx> propertyHelper 
        = new PropertyHelper<Xxxx>();
    
    public int Foo
    {
        get { return propertyHelper.GetPropertyValue(x => x.Foo); }     
    }