什么时候使用C#结构(值类型)牺牲性能?

sma*_*man 14 c# performance struct value-type non-nullable

我一直在使用结构作为隐式验证复杂值对象的机制,以及围绕更复杂类的通用结构以确保有效值.我对性能影响有点无知,所以我希望你们都能帮助我.例如,如果我要做一些事情,比如将域对象注入值类型包装器,会导致问题吗?为什么?我理解值类型和引用类型之间的区别,我的目标是利用值类型的不同行为.为了负责任地做到这一点,我到底需要考虑什么?

这是我正在思考的一个非常基本的想法.

public struct NeverNull<T>
    where T: class, new()
{

    private NeverNull(T reference)
    {
        _reference = reference;
    }

    private T _reference;

    public T Reference
    {
        get
        {
            if(_reference == null)
            {
                _reference = new T();
            }
            return _reference;
        }
        set
        {
            _reference = value;
        }
    }

    public static implicit operator NeverNull<T>(T reference)
    {
        return new NeverNull<T>(reference);
    }

    public static implicit operator T(NeverNull<T> value)
    {
        return value.Reference;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 11

好吧,有一件令人讨厌的事情是,这并不像你想象的那样天真:

NeverNull<Foo> wrapper1 = new NeverNull<Foo>();
NeverNull<Foo> wrapper2 = wrapper1;

Foo foo1 = wrapper1;
Foo foo2 = wrapper2;
Run Code Online (Sandbox Code Playgroud)

这将创建两个实例,Foo因为在wrapper1创建实例之前复制了原始版本.

基本上,你正在处理一个可变的结构 - 这几乎不是一件好事.另外,我一般不热衷于隐式转换.

感觉就像你正试图在这里实现看起来很神奇的代码......而且我通常会反对这种事情.也许它对你的特定用例有意义,但我想不出我个人想要使用它的地方.

  • @smartcaveman:听起来好像您在彼此之上添加了魔法层……我很少发现我对如果没有构建新的“空”实例感到满意否则存在。至于静态成员-不,它们在结构和类之间基本相同。 (2认同)

Eri*_*ert 7

正如Jon正确指出的那样,这里的问题是类型的行为是意外的,而不是它很.从性能角度来看,围绕引用的struct wrapper的开销应该非常低.

如果你想要做的是表示一个不可为空的引用类型,那么struct是一种合理的方法.但是,我倾向于通过失去"自动创建"功能使结构不可变:

public struct NeverNull<T> where T: class 
{ 
    private NeverNull(T reference) : this()
    { 
        if (reference == null) throw new Exception(); // Choose the right exception
        this.Reference = reference; 
    } 

    public T Reference { get; private set; }

    public static implicit operator NeverNull<T>(T reference) 
    { 
        return new NeverNull<T>(reference); 
    } 

    public static implicit operator T(NeverNull<T> value) 
    { 
        return value.Reference; 
    } 
}
Run Code Online (Sandbox Code Playgroud)

让呼叫者负责提供有效的参考; 如果他们想要"新"一个,那就让他们吧.

另请注意,通用转换运算符可以为您提供意外结果.您应该阅读转换运算符的规范并彻底理解它.例如,你不能围绕"对象"创建一个非null包装器,然后将该事物隐式转换为展开转换; 每个对对象的隐式转换都将在struct上进行装箱转换.您不能"替换"C#语言的内置转换.