Take the following code for exhibit A:
string sql;
if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
Documentation for Dictionary says that if the Key isn't found, the reference type will be set to null. OK, that's fine.
If the key is found, how is the variable 'sql' filled?
Is the found value for the key cloned?
Is a type of object for the item found created and then the contents of the object copied?
Is this safe?
Or, to set up a place for the outbound object to reside, should the call be set up as exhibit B:
var sql = string.Empty;
if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
Then the variable 'sql' is initialized and a safe place for the object exists.
(My question comes from my aversion of null pointers in my C programming days.)
In my view, it's better not to set it to a value. After all, that value is guaranteed to be replaced by the method call (assuming that doesn't throw an exception) so why bother specifying a value which is pointless? It just misleads the reader into thinking it makes a difference.
An out parameter is special in that the variable you use to provide a value for it doesn't have to be definitely-assigned before the call, but will be definitely-assigned after the call. Any value it has before the call will not be visible to the method.
(Note that ref parameters don't behave that way - they have to be definitely assigned beforehand.)
See my article on C# argument passing for more details on the different parameter modes in C#.
If the key is found, how is the variable 'sql' filled? Is the found value for the key cloned? Is a type of object for the item found created and then the contents of the object copied?
The value of the parameter within the method becomes the value of the variable in the callers code, in the same way as normal assignment. There is no object cloning going on.
If the key is found, how is the variable 'sql' filled?
You can think it like this:
var sql = GetQueries[type.TypeHandle];
Or, to set up a place for the outbound object to reside, should the call be set up as exhibit B:
No actually it doesn't matter. You don't have to initialize sql to a value, since you are passing it as out argument.The function guarantees that it will set the value of it's argument by declaring it as out.
Related
If I had a controller method like so:
public ActionResult<Item> GetItem(RequestHeaderBase headers, RequestObject request)
Can I always assume that the headers object and request object will be instantiated (not null)?
(I would appreciate any links to deeper reading regarding how this works regardless of the answer)
Here is an example of what I mean
Here I call the request: https://localhost:44360/weatherforecast and you can see the input object has been instantiated despite me not providing the query string.
Another example, this time with data from the body:
In both cases, you can see with my debugging, no matter what the objects are instantiated, even if I don't provide any data in the request.
Now I've seen code that checks if this data is null in some of our applications, I believe this code is irrelevant, and so can be removed.
if it is a reference object ( not a value) it will be always instantiated. Each object property will have a default value. This is why any class that are using for action input parameter should have default or parameter less constructor. Otherwise it will cause an exception. Only when object is created, controller trying to assign values to the object properties according to the http request. But be carefull, only the top level will be instantiated. If you have another complicated properties inside of this object, they will be instantied and have a default value according to the type. For example, List will be null (default value), not an empty list. Nullable integer property will ne null, not nullable 0, and so on. But you can assign default values you need in the constructor for example.
even if you change the action signature to this
public ActionResult<Item> GetItem(RequestHeaderBase headers=null, RequestObject request=null)
they still will be instantiated, each property will have a default value according to the type.
Can I always assume that the headers object and request object will be instantiated (not null)?
no, they can be null.
So I was reading Jon Skeet's C# in depth and came across some myths like Reference types are always passed by ref, So I decided to do a little experiment myself.
As you can see in the following code I have a simple Car class with one property which is initialized to 500 when the constructor is called. I also have the NullIt function which assigns null to the parameter value and a SpeedUp method which just changes the Speed property value.
Examining the main method you can see I instantiate a Car object, then I pass the object to the static SpeedUp method and the Speed value changes to 1000 but when I'm passing it to the also static NullIt method the object remains intact. From this the only thing I can assume is that the object is passed by value and the fields / properties are passed by reference. Is this right?
I know that if I pass it using the ref keyword will return null.
class Program
{
static void Main(string[] args)
{
Car c = new Car();
Car.SpeedUP(c);
Car.NullIt(c);
}
class Car
{
public int Speed { get; set; }
public Car() { Speed = 500; }
public static void SpeedUP(Car c)
{
c.Speed = 1000;
}
public static void NullIt(Car c)
{
c = null;
}
}
}
From this the only thing I can assume is that the object is passed by
value and the fields / properties are passed by reference. Is this
right?
Not really. Object's address is passed by value.
So when you do:
Car.SpeedUP(c);
Now your parameter of method SpeedUp and your field c in caller, both point to the same location in memory. Thus changing the property works.
But for your call:
Car.NullIt(c);
Your method parameter c and caller's c both points to same location. But since you assign null to your parameter c , now it is not pointing to any memory location, but the original/caller's c still points to the same memory location.
Consider the following:
When you pass parameter to your method then two references in memory would be pointing to the same address like:
But when you assign null it doesn't change the other reference.
The first reference (in caller) still points to the same location, only the method parameter is now pointing to null.
When you call NullIt() you are passing the value of a reference to your instance of Car. You then change this value to null. However, the original copy of that value remains intact. Exactly the same way passing the value of an int works - you can modify your local copy without affecting the "original".
If you were to change that to NullIt(ref Car c), you would be passing a reference to the reference, and hence setting it to null would set the original value of the reference to null. That last part can be a bit of a mind bender, but it's so rarely necessary (if ever) that you don't need to worry too much about it.
From this the only thing I can assume is that the object is passed by value and the fields / properties are passed by reference. Is this right?
While the observed effect may look like this, the way you describe it has some incorrect implications: The fields/properties are not "passed by reference" in a way that there is a reference to each field/property. It is rather that the reference to the object is passed by value.
That is why by accessing any member of the object, or the object itself, you are accessing the very same instance you passed into the method, not a copy thereof.
However, the variable c itself is a reference, and that reference is passed by value. That is why, in your methods, you cannot change that reference by assigning a new value and expect that the variable c itself now has a new value (in your case null).
It IS a bit confusing, because the word reference is overloaded (has two subtly different meanings).
When describing types as either value types or reference types. it means one thing,
Whether the data for the state of an object of that type is stored on the stack, (which is a section of memory that methods have access to) or whether it is stored on another section of memory called the Heap, and only the address of that section of heap is then stored on the stack, allowing code to only access the object indirectly.
When describing whether parameter values are passed to a method by value or by reference, otoh, it means something different. It means whether the actual value of the parameter is [copied and] passed to the method, or whether the address of the parameter value's memory slot is passed.
So you can actually have four combinations here:
Pass a value type by value - The value is copied and passed to
the method. The method cannot change the source value.
Pass a reference type by value. The address of the reference type
(which is on the Heap) is copied and passed. The method cannot
change the address in the source variable, but it CAN change the
data on the HEAP that the address points to.
Pass a value type by reference. The method gets the address of the
source object, (on the stack) and can change that source value.
Pass a reference type by reference. The method gets the address of
the variable (on the stack) that contains the address of the object
itself (on the Heap). The method can change the data in the source
object, AND CAN ALSO CHANGE WHICH OBJECT (ON THE HEAP) THE SOURCE
VARIABLE POINTS TO)
Quick question :
I am passing a class (reference type) to a method without using the "ref" keyword. Thus, the reference itself to my class is passed by value.
Then, I change the reference of my class (I make the reference point to another instance defined inside my method).
Finally, I return the initial method. However, in this case the returned instances points to the instance of the second class.
public Class Foo(Class A)
{
Class B = new Class();
A = B;
return A;
}
Foo returns a references pointing to B !
I am a little bit confused, since when doing A = B I make the reference of A point to another reference, or A's referenced is passed by value.
EDIT 1
Thanks for the response, but If I take the following example the change is not reflected. Indeed, I am trying to change the references of A but A's references is passed by value so in this case I understand why the change is not reflected..
void Foo(Class A)
{
A = null;
}
Many Thanks.
Basically, when you're passing an object, a reference is passed:
When an object of a reference type is passed to a method, a reference to the object is passed. That is, the method receives not the object itself but an argument that indicates the location of the object. If you change a member of the object by using this reference, the change is reflected in the argument in the calling method, even if you pass the object by value.
You can read more on this MSDN page.
You can read more on the other answers, but do notice that you're returning a class. Usually you'll return a specific object type, and when you won't be so free to do silly things like that. (of course, assuming that B inherits from A, you could create a new B inside the method and return it, which will be valid, but still, it's not making sense).
Another thing you might want to remember is the ref and out.
ref will expect an initialized value, and that value is changed in the method.
out doesn't care what it gets in, but you need to initialize and set it in the method.
Other than that, and the other answers here, either be more specific with your question and code, or have a read at the different links in the answers :)
You are passing a reference - an immutable value - into the method using the mutable variable, more precisely a parameter, A. Then you assign to the mutable variable A a new value, the immutable reference to the newly created object. Finally you are returning the current value of the variable A which at that point is the reference to the new object and no longer the reference to the object you passed into the method.
Essentially you are confusing the variable and the value stored in that variable. At no point did you change any reference, you only exchanged the value, i.e. reference, stored in the variable.
When you pass reference type by value the attempt to reassign the parameter to a different memory location only works inside the method and does not affect the original variable.
Check out Passing reference - type parameters .
I know structs are value types but then I do not understand why this works:
EDIT: I mean, why this.Size.Height does not work then?
struct A
{
int height;
public int Height
{
get
{
return height;
}
set
{
height = value;
}
}
}
//... class Main
{
A a = A();
a.Height = 5; //works. Why? I thought it should say "cannot modify as it is not variable". I think the properties should return copy of this struct...?
}
Second question - I have read I do not need to use "new" with structs but it does not work without it for me.
Let me break that down into several questions:
What is a variable?
A variable is a storage location that contains a value.
Why are value types called value types?
The value of a variable of value type is a value, and is copied by value. The value of a variable of reference type is a reference and is copied by reference. That is why value types are called value types and reference types are called reference types.
Why does a.Height = 10 work?
To change the value stored in a variable of reference type, you've got to have a variable to begin with. In this case, you do: you have the variable "a". The compiler compiles that as "pass the managed address of the variable 'a' to the Height setter with the argument 10". Therefore the Height property setter knows how to find the location of the value stored in 'a' and mutate it.
Why does a.Size.Height = 10 not work?
To change the value stored in a variable of reference type, you've got to have a variable to begin with. The expression "a.Size" is not a variable; it is a value. a.Size does not give you the variable that backs the property -- in fact, there might not be one. Instead, it gives you the value of the property. Value types are copied by value; this is a copy of the value of the property. This means that the compiler has two choices: it can copy the value into a temporary variable and then mutate that variable, tricking you into thinking that you've mutated the backing store of a.Size. Or, it can give you an error indicating that you're doing something foolish. It does the latter.
Isn't this confusing and vexing?
Yes. The moral of the story is do not make mutable value types. Only make immutable value types. Never have a setter on a value type in the first place; only do the assignment in the constructor. If the thing has to be mutable, make it a reference type.
Do I have to use "new" to make a new instance of a value type?
No. You can also use "default":
Foo f = default(Foo);
If Foo is a value type then this fills in the contents of storage location f with the Foo which has all its fields set to their default values.
Or, if the value type is mutable, you can simply set the values of all the fields. However, you have to set all of them if you do not use a constructor or the default initializer. You have to set all of them including private fields.
But if a struct has all public fields doesn't that violate best practices guidelines in two ways? First, because it has public fields, and second, because it is a mutable value type?
Yes. Don't do that.
I think you are confusing value types with immutability. I think this SO question will help you.
"this.Size.Height = 5" does not work because when a value type is used as the type of a property, the above line of code would actually mean "this.get_Size().set_Height(5)", and the result of the get_Size() call is a copy of the original, being a value type.
Thus, were it allowed by C#, setting the property value to 5 would change the copy's value rather than the original property value, which is highly undesirable.
Of course, this does not apply when the value type property of a class is changed via a local variable, so this scenario can be safely be supported.
It is very normal. And why you think that it must not let you to set value for Height ?
It is very normal behavior how properties should work.
Concerning calling new, yes it is not mandatory for value types. For value types it just calls default constructor which just initializes fields with default values.
A property will return the value that its "get" method returns. It's the same for structs and classes. If you provider a "set" method, it will do whatever the "set" indicates. The only way that Height could not be modified would be either to use a private "set", or not provide a "set" at all.
I'm implementing a TryParse(string s, Out object result) method.
If the parse fails, I would like not to touch the out parameter so any previous result will remain intact.
But VS2k8 won't let me. I have to set the value of the out object no matter what.
Should I just put result = result for the sake of pleasing the compiler? Am I missing something?
Assign null (or default(T) more generally). You must assign a value, that's what 'out' means.
Your suggestion of result = result won't work, because it's an out parameter - it's not definitely assigned to start with, so you can't read its value until you've assigned a value to it.
result = null;
is definitely the right way to go for an object out parameter. Basically use default(T) for whatever type T you've got. (The default operator is useful in generic methods - for non-generic code I'd normally just use null, 0, whatever.)
EDIT: Based on the comment from Boris, it may be worth elaborating on the difference between a ref parameter and an out parameter:
Out parameters
Don't have to be definitely assigned by the caller
Are treated as "not definitely assigned" at the start of the method (you can't read the value without assigning it first, just like a local variable)
Have to be definitely assigned (by the method) before the method terminates normally (i.e. before it returns; it can throw an exception without assigning a value to the parameter)
Ref parameters
Do have to be definitely assigned by the caller
Are treated as "definitely assigned" at the start of the method (so you can read the value without assigning it first)
Don't have to be assigned to within the method (i.e. you can leave the parameter with its original value)
result = null;
Just put some default value. For example the Int32.TryParse method puts zero.
You could use ref instead of out if you don't want to assign a value, although this must then be initialised by the caller.
You could throw an exception before the code that is supposed to set result.