参数参数有什么不好?

时间:2009-02-20 17:18:37

标签: c# oop

我遇到的情况我认为只能通过使用ref参数来解决。但是,这意味着当我只需要5%的时间参考ref参数提供的功能时,将方法更改为始终接受ref参数。

这让我觉得“哇,疯了,必须找到另一种方式”。我是傻瓜吗? 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 个答案:

答案 0 :(得分:28)

你能加一个过载吗?有一个没有ref参数的签名,还有一个签名。

参考参数可以有用,我很高兴它们存在于C#中,但不应该不加思索地使用它们。通常,如果方法有效地返回两个值,则将方法拆分为两个部分或将两个值封装在单个类型中会更好。这些都不包括每一个案例 - 肯定有时ref是最佳选择。

答案 1 :(得分:13)

或许对 5%案例使用重载功能,并保持其他功能不变。

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

答案 2 :(得分:5)

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

现在,显然,只有在您愿意将返回值放入API时才可行。此外,如果您的函数已经返回一个值,则会遇到创建包含原始返回值以及新更改的对象的自定义结构的可能性。

最终,这取决于您以及如何记录您的API。我根据自己的经验发现,我的同事们往往希望我的函数“像.NET Framework函数一样”。 :)

答案 3 :(得分:4)

ref参数本身不会导致问题。这是该语言的文档特征。

然而,它可能会导致社会问题。具体而言,您的API的客户可能不会期望ref参数,因为它很少见。您可以修改客户端不期望的引用。

当然,你可以争辩说,这是客户的错误,因为你没有阅读你的API规范,这是真的。但有时候最好减少意外。编写好的代码不仅仅是遵循规则和记录内容,还包括为人类用户提供显而易见自然的东西。

答案 4 :(得分:2)

重载不会终止您的应用程序或其设计。只要意图明确记录,就应该没问题。

可以考虑的一件事是通过不同的类型参数减轻您对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; } 
}

在您的代码中,您只需传递一个SaveArgs而不是TEntity,以便您可以使用更有意义的信息修改其属性。 (当然,它比我上面提到的设计更好。)但是你不必担心模糊的方法接口,你可以在一个冗长的类中返回你需要的尽可能多的数据。

只是一个想法。

编辑:修正了代码。我的坏。

答案 5 :(得分:1)

使用我能想到的ref参数没有任何问题,实际上它们有时非常方便。我认为他们有时会因为调试而变得糟糕,因为变量的值可能会在代码逻辑中发生变化,有时很难跟踪。转换到像WebServices这样只能通过“值”的东西就足够了。

答案 6 :(得分:1)

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

大多数时候我在TryGet场景中使用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

答案 7 :(得分:1)

我在ref参数中看到的最常见的用法是返回多个值。如果是这种情况,您应该考虑创建一个将所有值作为一个对象返回的类或结构。 如果您仍想使用ref但想要使其成为可选项,请添加函数重载。

答案 8 :(得分:1)

ref只是一个工具。你应该想一想:我正在建造什么是最好的设计模式?

  • 有时候使用重载方法会更好。

  • 其他人最好返回自定义类型或元组。

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

  • 其他参考将是正确的决定。

答案 9 :(得分:0)

如果你的方法在5%的时间内只需要这个ref参数,那么你可能需要打破这个方法。当然没有更多的细节很难说,但这对我来说就像一个违反单一责任主体的情况。也许超载会有所帮助。

关于你的问题,我认为传递一个参数作为参考没有问题,尽管这不是常见的事情。

答案 10 :(得分:0)

这是F#或其他函数式编程语言在返回Tuple值时更好地解决的问题之一。它的语法更清晰,更简洁。在我正在阅读F#的书中,它实际上指出使用ref的C#equivelant在C#中执行相同的操作以返回多个参数。

我不知道这是不好的做法,还是有一些关于ref参数的潜在“booga booga”,对我来说,他们只觉得语法不干净。

答案 11 :(得分:0)

带有单个参考参数的void返回函数对我来说当然很有趣。如果我正在审查这段代码,我建议重构BeforeSave()以包含对Repository.Save()的调用 - 显然是重命名它。为什么不只是有一个方法来获取可能新的实体并保证一切都得到妥善保存?无论如何,调用者对返回的实体都没有做任何事情。