0

最近我发现自己越来越养成通过引用传递事物的习惯。我一直被教导通过 ref 传递“通常”是一个坏主意,因为跟踪可能影响您的对象的东西比较棘手,所以我想发布一个问题:“通过引用传递的缺点是什么? '

我最近通过引用传递的一个示例是视图状态中的惰性实例化对象。在我的代码隐藏中,我有一个带有公共属性的私有字段,它使用了一个辅助方法。目前的实现如下:

ASPX 代码隐藏

/// <summary>
/// Private field member for MyObject
/// </summary>
private Foobar _myObject = null;

/// <summary>
/// Gets or sets the current object
/// </summary>
public Foobar MyObject
{
    get
    {
        return this.ViewState.GetValue("MyObject", new Foobar(), ref this._myObject);
    }
    set
    {
        this.ViewState.SetValue("MyObject", value, ref this._myObject);
    }
}

这旨在替换if针对类中的字段和惰性实例化对象的大量重复分配检查。例如,如果没有辅助类,它将类似于。

/// <summary>
/// Private field member for MyObject
/// </summary>
private Foobar _myObject = null;

/// <summary>
/// Gets or sets the current object
/// </summary>
public Foobar MyObject
{
    get
    {
        if (this._myObject != null)
        {
            return this._myObject;
        }

        var viewStateValue = this.ViewState["MyObject"];
        if (viewStateValue == null || !(viewStateValue is Foobar))
        {
            this.ViewState["MyObject"] = new Foobar();
        }

        return this._myObject = (Foobar)this.ViewState["MyObject"];
    }
    set
    {
        this._myObject = value;
        this.ViewState["MyObject"] = value;
    }
}

两个代码片段都实现了相同的目标。第一种方法是集中一切,这是一件好事,但是它是通过引用传递的,在这种情况下我不确定这是一个好主意吗?

非常感谢任何建议和/或经验。

编辑GetValueandSetValue是 ViewState 上的扩展方法 。下面提供了代码。

/// <summary>
/// Gets a value from the current view state, if the type is correct and present
/// </summary>
public static T GetValue<T>(this StateBag source, string key, T @default)
{
    // check if the view state object exists, and is of the correct type
    object value = source[key];
    if (value == null || !(value is T))
    {
        return @default;
    }

    // return the object from the view state
    return (T)source[key];
}

/// <summary>
/// Sets the key value within the view state
/// </summary>
public static void SetValue<T>(this StateBag source, string key, T value)
{
    source[key] = value;
}

/// <summary>
/// Gets a value from the reference field helper, or the current view state, if the type is correct and present
/// </summary>
/// <returns>Returns a strongly typed session object, or default value</returns>
public static T GetValue<T>(this StateBag source, string key, T @default, ref T fieldHelper)
{
    return fieldHelper != null ? fieldHelper : fieldHelper = source.GetValue(key, @default);
}

/// <summary>
/// Sets the key value within the view state and the field helper
/// </summary>
/// <param name="value">The value</param>
public static void SetValue<T>(this StateBag source, string key, T value, ref T fieldHelper)
{
    source[key] = value;
    fieldHelper = value;
}
4

3 回答 3

1

在这种情况下,只需考虑一些选项。

您可以使用更少的代码行且无需引用来实现相同的结果:

get
{
    if (this._myObject == null)
        this._myObject = this.ViewState.GetValue<Foobar>("MyObject", new Foobar());
    return this._myObject;
}

ViewState.GetValue 如果存在则从 ViewState 返回对象,或者设置并返回默认值 (new FooBar())。我认为这是进行惰性属性初始化的非常规范的方法(或者您也可以在 .Net 4.0 中使用 Lazy)。您甚至可以将其压缩为一行:

return this._myObject = this._myObject ?? this.ViewState.GetValue("MyObject", new Foobar())

此外,您可以通过设置私有字段的 Action 而不是通过 ref 传递:

this.ViewState.GetValue("MyObject", new Foobar(), newValue => this._myObject = newValue);

我认为这样 ViewState 和 Foobar 的耦合度较低。

而且,您可以传递 () => Foorbar() (或 Lazy),而不是每次都为默认值创建 new Foorbar(),这样它只会在需要时创建一次。

因此,至少对于您的情况,我没有看到任何使用 ref 的充分理由。

于 2012-04-17T10:03:19.533 回答
1

感谢 Dave,忍不住尝试了 Lazy<> 类 :-)。

public class Foobar
{
}

public class ViewState
{
  private readonly Lazy<Foobar> _foobar = new Lazy<Foobar>();

  public Foobar LazyFoobar
  {
    get { return _foobar.Value; }
  }
}

// Gets or creates the foobar
Foobar lazyFoobar = this.ViewState.LazyFoobar;

为 for 实现一个类ViewState将具有以下优点:

  1. 这是类型安全的
  2. 懒加载容易集成
  3. 不需要缓存对象(更稳定)
  4. 代码可读
  5. 代码很快(没有类型转换)

回答您最初的问题:传递引用允许其他代码替换对象。我们必须相信被调用的函数,它不会将此引用传递给其他对象,并在以后随时替换原始对象。

于 2012-04-17T11:58:18.597 回答
-1

强制通过引用仅对原始对象(如字符串或 int)感兴趣。

如果不使用 ref,则仅在函数中传递值,但指向内存中的不同对象。

如果您使用“ref”或不使用“ref”,那么像类这样的复杂对象总是通过引用传递......这根本没有区别;-)

于 2012-04-17T09:54:06.413 回答