代码之家  ›  专栏  ›  技术社区  ›  Garry Shutler

ref参数有什么不好?

  •  14
  • Garry Shutler  · 技术社区  · 17 年前

    我面临一个我认为只能通过使用ref参数来解决的情况。然而,这将意味着当我只需要ref参数提供的功能时,将方法更改为始终接受ref参数5%。

    这让我想到“哇,疯了,一定要找到另一种方法”。我是不是很愚蠢?ref参数会导致什么类型的问题?

    编辑

    我想要保存一个新实例(它将使用稍后可能使用的ID进行更新),或者检索一个与某些逻辑匹配的现有实例并进行更新,保存它,然后更改新实例的引用以指向现有实例。

    代码可能会更清楚:

    protected override void BeforeSave(Log entity)
    {
        var newLog = entity;
    
        var existingLog = (from log in repository.All()
                               where log.Stuff == newLog.Stuff 
                                     && log.Id != newLog.Id
                               select log).SingleOrDefault();
    
        if (existingLog != null)
        {
            // update the time
            existingLog.SomeValue = entity.SomeValue;
            // remove the reference to the new entity
            entity = existingLog;
        }
    }
    
    // called from base class which usually does nothing before save
    public void Save(TEntity entity)
    {
        var report = validator.Validate(entity);
    
        if (report.ValidationPassed)
        {
            BeforeSave(entity);
            repository.Save(entity);
        }
        else
        {
            throw new ValidationException { Report = report };
        }
    }
    

    事实上,我将只为基类的一个子类(到目前为止)添加它,这阻止了我使用重载(因为我必须复制Save方法)。我也有一个问题,我需要强迫他们在这个例子中使用ref版本,否则事情将不会像预期的那样工作。

    12 回复  |  直到 17 年前
        1
  •  30
  •   Jon Skeet    17 年前

    你能加一个重载吗?一个签名没有ref参数,另一个签名有ref参数。

    参考参数 ref 这是最好的选择。

        2
  •  13
  •   Chris Ballance    17 年前

    也许使用 重载函数 5%病例 另一个函数保持不变。

    不必要的ref参数可能会导致糟糕的设计模式,但如果您有特定的需求,则 没问题 做这件事。

        3
  •  5
  •   Mike    17 年前

    如果将.NET框架作为人们对API的期望的晴雨表,那么请考虑几乎所有的字符串方法。 返回修改后的值 ,但保持传递的参数不变。例如,String.Trim()返回经过修剪的字符串-它不会修剪作为参数传入的字符串。

    现在,很明显,只有当您愿意将返回值放入API中时,这才是可行的。另外,如果您的函数已经返回了一个值,那么您就有可能创建一个包含原始返回值和新更改的对象的自定义结构。

        4
  •  4
  •   Jason Cohen    17 年前

    A. ref 参数本身不会导致问题。这是语言的一个有记录的特征。

    然而,它可能会引起社会问题。具体来说,API的客户端可能不希望 裁判

    对人类用户来说是显而易见的。

        5
  •  2
  •   Mike Hofer    17 年前

    可以考虑的一件事是减轻你对未来的恐惧 ref 参数通过不同的 类型 参数。例如,考虑一下:

    public class SaveArgs
    {
       public SaveArgs(TEntity value) { this.Value = value; }
    
       public TEntity Value { get; private set;}
       public int NewId { get; internal set; }
       public bool NewIdGenerated { get; internal set; } 
    }
    

    只是一个想法。

    编辑:修复了代码。我的错。

        6
  •  1
  •   Mark Kadlec    17 年前

    使用我能想到的ref参数并没有什么错,事实上它们有时非常方便。我认为他们有时会因为调试而受到不好的批评,因为变量的值可能会在代码逻辑中发生变化,有时很难跟踪。当转换到像WebService这样只传递“值”就足够的服务时,这也会使事情变得困难。

        7
  •  1
  •   JaredPar    17 年前

    关于ref参数,我遇到的最大问题是它们使使用类型推断变得困难,因为有时必须显式声明用作ref参数的变量的类型。

    例如。字典中的TryGetValue从

    bool TryGetValue(TKey key, out TValue value)
    

    Option<Value> TryGetValue(TKey key)
    

    http://blogs.msdn.com/jaredpar/archive/2008/10/08/functional-c-providing-an-option-part-2.aspx

        8
  •  1
  •   Whatsit    17 年前

    如果仍然希望使用ref,但希望使其成为可选的,请添加函数重载。

        9
  •  1
  •   Adrian Lopez    11 年前

    ref只是一个工具。你应该想一想:对于我正在构建的东西,什么是最好的设计模式?

    • 其他人最好使用全局变量。

    • 而其他裁判将是正确的决定。

        10
  •  0
  •   JoshBerke    17 年前

    如果您的方法在5%的时间里只需要这个ref参数,那么您可能需要分解这个方法。当然,没有更多的细节,这很难说,但在我看来,这似乎是一个违反单一责任原则的案例。也许超载会有所帮助。

        11
  •  0
  •   Bart Czernicki    17 年前

    这是F#或其他函数式编程语言通过返回元组值更好地解决的问题之一。它的语法更加简洁明了。在我正在读的关于F#的书中,它实际上指出了使用ref的C#等价于在C#中做同样的事情来返回多个参数。

        12
  •  0
  •   MNGwinn    17 年前

    在我看来,带有单个引用参数的void返回函数确实很有趣。如果我正在查看这段代码,我建议重构BeforeSave()以包含对Repository.Save()的调用——显然是重命名它。为什么不使用一种方法来获取可能的新实体并保证所有内容都正确保存?调用方无论如何都不会对返回的实体执行任何操作。