Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I detect whether I've been given a new object as a parameter?

Short Version

For those who don't have the time to read my reasoning for this question below:

Is there any way to enforce a policy of "new objects only" or "existing objects only" for a method's parameters?

Long Version

There are plenty of methods which take objects as parameters, and it doesn't matter whether the method has the object "all to itself" or not. For instance:

var people = new List<Person>();

Person bob = new Person("Bob");

people.Add(bob);
people.Add(new Person("Larry"));

Here the List<Person>.Add method has taken an "existing" Person (Bob) as well as a "new" Person (Larry), and the list contains both items. Bob can be accessed as either bob or people[0]. Larry can be accessed as people[1] and, if desired, cached and accessed as larry (or whatever) thereafter.

OK, fine. But sometimes a method really shouldn't be passed a new object. Take, for example, Array.Sort<T>. The following doesn't make a whole lot of sense:

Array.Sort<int>(new int[] {5, 6, 3, 7, 2, 1});

All the above code does is take a new array, sort it, and then forget it (as its reference count reaches zero after Array.Sort<int> exits and the sorted array will therefore be garbage collected, if I'm not mistaken). So Array.Sort<T> expects an "existing" array as its argument.

There are conceivably other methods which may expect "new" objects (though I would generally think that to have such an expectation would be a design mistake). An imperfect example would be this:

DataTable firstTable = myDataSet.Tables["FirstTable"];
DataTable secondTable = myDataSet.Tables["SecondTable"];

firstTable.Rows.Add(secondTable.Rows[0]);

As I said, this isn't a great example, since DataRowCollection.Add doesn't actually expect a new DataRow, exactly; but it does expect a DataRow that doesn't already belong to a DataTable. So the last line in the code above won't work; it needs to be:

firstTable.ImportRow(secondTable.Rows[0]);

Anyway, this is a lot of setup for my question, which is: is there any way to enforce a policy of "new objects only" or "existing objects only" for a method's parameters, either in its definition (perhaps by some custom attributes I'm not aware of) or within the method itself (perhaps by reflection, though I'd probably shy away from this even if it were available)?

If not, any interesting ideas as to how to possibly accomplish this would be more than welcome. For instance I suppose if there were some way to get the GC's reference count for a given object, you could tell right away at the start of a method whether you've received a new object or not (assuming you're dealing with reference types, of course--which is the only scenario to which this question is relevant anyway).


EDIT:

The longer version gets longer.

All right, suppose I have some method that I want to optionally accept a TextWriter to output its progress or what-have-you:

static void TryDoSomething(TextWriter output) {
    // do something...
    if (output != null)
        output.WriteLine("Did something...");
    
    // do something else...
    if (output != null)
        output.WriteLine("Did something else...");
    
    // etc. etc.
    
    if (output != null)
        // do I call output.Close() or not?
}

static void TryDoSomething() {
    TryDoSomething(null);
}

Now, let's consider two different ways I could call this method:

string path = GetFilePath();
using (StreamWriter writer = new StreamWriter(path)) {
    TryDoSomething(writer);

    // do more things with writer
}

OR:

TryDoSomething(new StreamWriter(path));

Hmm... it would seem that this poses a problem, doesn't it? I've constructed a StreamWriter, which implements IDisposable, but TryDoSomething isn't going to presume to know whether it has exclusive access to its output argument or not. So the object either gets disposed prematurely (in the first case), or doesn't get disposed at all (in the second case).

I'm not saying this would be a great design, necessarily. Perhaps Josh Stodola is right and this is just a bad idea from the start. Anyway, I asked the question mainly because I was just curious if such a thing were possible. Looks like the answer is: not really.

like image 484
Dan Tao Avatar asked Dec 17 '09 15:12

Dan Tao


3 Answers

No, basically.

There's really no difference between:

var x = new ...;
Foo(x);

and

Foo(new ...);

and indeed sometimes you might convert between the two for debugging purposes.

Note that in the DataRow/DataTable example, there's an alternative approach though - that DataRow can know its parent as part of its state. That's not the same thing as being "new" or not - you could have a "detach" operation for example. Defining conditions in terms of the genuine hard-and-fast state of the object makes a lot more sense than woolly terms such as "new".

like image 196
Jon Skeet Avatar answered Oct 17 '22 15:10

Jon Skeet


Yes, there is a way to do this.

Sort of.

If you make your parameter a ref parameter, you'll have to have an existing variable as your argument. You can't do something like this:

DoSomething(ref new Customer());

If you do, you'll get the error "A ref or out argument must be an assignable variable."

Of course, using ref has other implications. However, if you're the one writing the method, you don't need to worry about them. As long as you don't reassign the ref parameter inside the method, it won't make any difference whether you use ref or not.

I'm not saying it's good style, necessarily. You shouldn't use ref or out unless you really, really need to and have no other way to do what you're doing. But using ref will make what you want to do work.

like image 31
Ryan Lundy Avatar answered Oct 17 '22 13:10

Ryan Lundy


No. And if there is some reason that you need to do this, your code has improper architecture.

like image 41
Josh Stodola Avatar answered Oct 17 '22 15:10

Josh Stodola