What's so bad about ref parameters?

2019-02-12 01:28发布

I'm faced with a situation that I think can only be solved by using a ref parameter. However, this will mean changing a method to always accept a ref parameter when I only need the functionality provided by a ref parameter 5% of the time.

This makes me think "whoa, crazy, must find another way". Am I being stupid? What sort of problems can be caused by a ref parameter?

Edit

Further details were requested, I don't think they are entirely relevant to what I was asking but here we go.

I'm wanting to either save a new instance (which will update with the ID which may later be used) or retrieve an existing instance that matches some logic and update that, save it then change the reference of the new instance to point to the existing one.

Code may make it clearer:

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 };
    }
}

It's the fact that I would be adding it in only for one child (so far) of the base class that prevents me using an overload (due to the fact I would have to duplicate the Save method). I also have the problem whereby I need to force them to use the ref version in this instance otherwise things won't work as expected.

标签: c# oop
12条回答
三岁会撩人
2楼-- · 2019-02-12 01:47

An overload won't kill your application or its design. As long as the intent is clearly documented, it should be okay.

One thing that might be considered is mitigating your fears about the ref parameter through a different type of parameter. For example, consider this:

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; } 
}

In your code, you simply pass a SaveArgs rather than the TEntity, so that you can modify its properties with more meaningful information. (Naturally, it'd be a better-designed class than what I have above.) But then you wouldn't have to worry about vague method interfaces, and you could return as much data as you needed to in a verbose class.

Just a thought.

EDIT: Fixed the code. My bad.

查看更多
三岁会撩人
3楼-- · 2019-02-12 01:47

The most common use I've seen for ref parameters is as a way of returning multiple values. If that's the case, you should consider creating a class or struct that returns all the values as one object. If you still want to use a ref but want to make it optional, add a function overload.

查看更多
We Are One
4楼-- · 2019-02-12 01:47

If your method only needs this ref parameter 5% of the time perhaps you need to break this method down. Of course without more details its hard to say but this to me smells like a case of violating single responsability principal. Perhaps overloading it will help.

As for your question there is no issue in my opinion passing a parameter as a reference although it is not a common thing to run into.

查看更多
叛逆
5楼-- · 2019-02-12 01:51

A void-returning function with a single reference parameter certainly looks funny to me. If I were reviewing this code, I'd suggest refactoring the BeforeSave() to include the call to Repository.Save() - renaming it, obviously. Why not just have one method that takes your possibly-new entity and guarantees that everything is saved properly? The caller doesn't do anything with the returned entity anyway.

查看更多
贪生不怕死
6楼-- · 2019-02-12 01:53

If you take the .NET Framework as a barometer of people's expectations of an API, consider that almost all of the String methods return the modified value, but leave the passed argument unchanged. String.Trim(), for instance, returns the trimmed String - it doesn't trim the String that was passed in as an argument.

Now, obviously, this is only feasible if you're willing to put return-values into your API. Also, if your function already returns a value, you run into the nasty possibility of creating a custom structure that contains your original return value as well as the newly changed object.

Ultimately, it's up to you and how you document your API. I've found in my experience though that my fellow programmers tend to expect my functions to act "like the .NET Framework functions". :)

查看更多
萌系小妹纸
7楼-- · 2019-02-12 01:54

ref is just a tool. You should think: What is the best design pattern for what I am building?

  • Sometimes will be better to use an overloaded method.

  • Others will be better to return a custom type or a tuple.

  • Others will be better to use a global variable.

  • And others ref will be the right decision.

查看更多
登录 后发表回答