开发者

When is using the C# ref keyword ever a good idea?

The more I see ref used in production code, the more misuse I encounter and the more pain it causes me. I have come to hate this keyword, because from a framework-building standpoint, it seems silly. When would it be a good idea to communicate to users of your code the notion of maybe changing an object reference/value out from beneath them?

By contrast, I love out keywords and I love even more when no keywords are used at all, in both cases because of the guarantees you're given when using them. Ref on the other hand makes no guarantees, except that you'll be forced to initialize the parameter before you pass it in, even though nothing may be changed about it.

I'm no sage developer though; I'm sure it's got practically applicable uses.开发者_运维百科 I'd just like to know what they are.


The Framework Design Guidelines (a book by Krzysztof Cwalina and Brad Abrams) recommend to avoid both ref and out parameters.

AVOID using out or ref parameters.

Using out or ref parameters requires experience with pointers, understanding how value types and reference types differ, and handling methods with multiple return values. Also, the difference between out and ref parameters is not widely understood. Framework architects designing for a general audience should not expect users to master working with out or ref parameters.

The Framework Design Guidelines cite the canonical Swap method as a valid exception:

void Swap<T>(ref T obj1, ref T obj2)
{
    T temp = obj1;
    obj1 = obj2;
    obj2 = temp;
}

but at the same time a comment remarks

Swap always comes up in these discussions, but I have not written code that actually needed a swap method since college. Unless you've got a very good reason, avoid out and ref altogether.


Most of the Interlocked methods use ref parameters for (I’m sure you agree) good reason.


I try to avoid it on public APIs, but it definitely has uses. Mutable value-types is an important one, especially on things like CF (where mutable structs are more common, due to platform requirements). However, perhaps the most common time I use it is when refactoring parts of a complex algorithm out into a few methods, where a state object is overkill and I need to pass multiple values around:

i.e.

var x = .....
var y = .....
// some local code...
var z = DoSomethingSpecific(ref x, ref y); // needs and updates x/y
// more local code...

etc. Where DoSomethingSpecific is a private method, just moved out to keep method responsibility manageable.


Any time you want to change the value of a value type - this happens a lot in cases where you want to efficiently update a pair of related values (i.e. rather than returning a struct containing two ints, you pass (ref int x, ref int y))


Maybe when you have a struct (which is a value type):

struct Foo
{
    int i;

    public void Test()
    {
        i++;
    }
}

static void update(ref Foo foo)
{
    foo.Test();
}

and

Foo b = new Foo();
update(ref b);

Here you would to use two-parameters with out like:

static void update(Foo foo, out Foo outFoo) //Yes I know you could return one foo instead of a out but look below
{
    foo.Test();

    outFoo = foo;
}

imaging the method having more than one Foo then you would get twice the parameters with out versus ref. An alternative is to return a N-tuple. I don't have a real-world example on when to use this stuff.

Add on: Different .TryParse methods could also have avoided out if they returned Nullable<T> instead which essentially is a tuple of boolean * T.


How about if one wishes to pass an array to a function which might or might not change its size and do something else to it. Often, one would wrap the array in another object, but if one wishes to handle the array directly passing by reference would seem the most natural approach.


It's useful when you need efficient in-place algorithms on bignums.


Hypothetically, I'd guess that you might use a lot of ref/out arguments if you intended to mimic the architecture of older procedural software, for example old game engines and so on. I've scanned the source code of one, I think it was Duke Nukem 3D, and it's procedural with lots of subroutines modifying variables in place, and almost no functions. Obviously, you'd be unlikely to program like this for a real production application unless you had some specific aim in mind.


I'm using ref quite often. Just think about functions with multiple return values. It doesn't make sense to create a return object (helper object) or even using hashtables for this purpose.

Example:

 getTreeNodeValues(ref selectedValue, ref selectedText);

Edit:

It's better to use out here - as commented.

 getTreeNodeValues(out selectedValue, out selectedText);

I'm using it for processing objects:

MyCar car = new MyCar { Name="TestCar"; Wieght=1000; }

UpdateWeight(ref car, 2000);


Another useful example in addition to swap<> is this:

Prompter.getString("Name ? ", ref firstName);
Prompter.getString("Lastname ? ", ref lastName);
Prompter.getString("Birthday ? ", ref firstName);
Prompter.getInt("Id ? ", ref id);
Prompter.getChar("Id type: <n = national id, p = passport, d = driver licence, m = medicare> \n? ", ref c);



public static class Prompter
{
    public static void getKey(string msg, ref string key)
    {
        Console.Write(msg);
        ConsoleKeyInfo cki = Console.ReadKey();
        string k = cki.Key.ToString();
        if (k.Length == 1)
            key = k;
    }

    public static void getChar(string msg, ref char key)
    {
        Console.Write(msg);
        key = Console.ReadKey().KeyChar;
        Console.WriteLine();
    }

    public static void getString(string msg, ref string s)
    {
        Console.Write(msg);
        string input = Console.ReadLine();
        if (input.Length != 0)
            s = input;
    }

    public static void getInt(string msg, ref int i)
    {
        int result;
        string s;

        Console.Write(msg);
        s = Console.ReadLine();

        int.TryParse(s, out result);
        if (result != 0)
            i = result;       
    }

    // not implemented yet
    public static string getDate(string msg)
    {
        // I should use DateTime.ParseExact(dateString, format, provider);
        throw new NotImplementedException();
    }    


}

Use out here it's not an option

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜