New-ing string types - c#

From Pro C#
Referring to "New-ing" Intrinsic data types...
All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
[...] Object references (including strings) are set to null.
In C#, strings do not have a public default constructor. My guess is that due to string's immutability, they have a private default constructor. But, the context here is talking about Object references and string's as a whole while using new.
Because one cannot do
String myString = new String();
So,
String a;
referencing string doesn't result in a "default value". Instead, it's a compiler error to access a.
Though
public class StringContainer
{
public static string myString { get; set; }
}
Results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
What is occurring in the StringContainer scenerio? Because there appears to be no new-able default constructor in string, is this an error in the C# book?

All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
There are an impressive number of subtle errors in that statement.
First off, there is no such thing as an "intrinsic" data type; perhaps this term is defined somewhere else in the book?
Second, it would be more accurate to say that all struct types have a public parameterless constructor called the "default" constructor. Some class types also have a public parameterless ctor; if you do not provide any ctor then the C# compiler will automatically generate a public parameterless ctor for you. If you do provide a ctor then the C# compiler will not do this for you.
Third, constructors do not create variables. The author is conflating a bunch of related but different things: the "new" operator, the memory manager, the constructor and the variable, and the created object. Variables are storage locations and are managed by the CLR; they are not created by the "new" operator.
The correct statement is that the "new" operator on a struct causes a variable to be created by the CLR on the temporary storage pool; that variable is then initialized by the memory manager, and then passed to the constructor for more initialization. The value thus created is then copied somewhere else. The "new" operator on a class causes the CLR to create an object on the long-term storage pool, and then pass a reference to that object to the CLR. No "variable" need be involved.
Confusing variables with objects is a very common error; it is valuable to understand the difference.
In C#, strings do not have a public default constructor.
Correct.
My guess is that due to string's immutability, they have a private default constructor.
Good guess, but wrong. There is no private parameterless constructor on string.
[an auto-property or field of type string] results in a legally accessible string (defaulted to null). This doesn't use new. It performs some kind of magical construction.
It does no such thing. A null reference is not a constructed object at all. It's the absence of a constructed object!
You're basically saying that my empty garage contains a "magically constructed" non-existing car. That is an exceedingly weird way to look at an empty garage; an empty garage contains no car at all, not a magically constructed non-existing car.
What is occurring in the StringContainer scenerio?
The containing type contains a compiler-generated field -- a variable -- of type string. Let's suppose the containing type is a struct or class. When the storage for the struct or class is initialized by the memory manager, the memory manager writes a null reference into the storage location associated with the variable.
Finally: I suspect your confusion is because you've gotten the "default constructor" and the "default value of a type" confused. For a struct, they are the same thing:
int x = new int();
and
int x = default(int);
both make an int initialized to zero.
For a class, they do not do the same thing:
Fruit f = new Fruit();
makes a new fruit reference and assigns the reference to variable f, whereas:
Fruit f = default(Fruit);
is the same as
Fruit f = null;
No constructor is called.

All intrinsic data types support what is known as a default constructor. It allows you to create a variable using the new keyword.
I'm not sure what the author means by "intrinsic data types". My best guess is that he actually means "value types" (i.e. types declared with C#'s struct keyword), because value types always have a default constructor, and reference types may not.
So if you have a field whose type is a struct type (e.g. Int32, CancellationToken), then that field will be initialized as if the type's default constructor was called.
In actual implementation, there's probably not an actual call to the type's default constructor -- the memory is just initialized to all zeroes, which is the same thing that would happen if you did call the default constructor. (That's why you can't provide your own parameterless constructor for a value type -- the parameterless constructor always initializes the memory to all zeroes. This greatly simplifies things like new int[10000] -- the compiler doesn't actually have to call new Int32() 10,000 times; it just zeroes out the memory.)
Your question about a string field in a class isn't really related to the author's discussion of "intrinsic data types", because both string and your enclosing class are reference types, not value types. So your class won't have a parameterless-constructor-that-you-can't-override; it will just have normal constructors. But the zeroing-out behavior is still there: when you call a constructor, the new memory block is zeroed out before the constructor code starts to run. Your string field is a reference type, and a zero reference is null.

I imagine it is using default(string) which returns null because string is a reference type.
Also, remember that constructors can't return null.

Related

Why does setting a base class equal to a derived type only work within scope of where it is set?

I found it difficult to come up with a descriptive enough title for this scenario so I'll let the code do most of the talking.
Consider covariance where you can substitute a derived type for a base class.
class Base
{
}
class Derived : Base
{
}
Passing in typeof(Base) to this method and setting that variable to the derived type is possible.
private void TryChangeType(Base instance)
{
var d = new Derived();
instance = d;
Console.WriteLine(instance.GetType().ToString());
}
However, when checking the type from the caller of the above function, the instance will still be of type Base
private void CallChangeType()
{
var b = new Base();
TryChangeType(b);
Console.WriteLine(b.GetType().ToString());
}
I would assume since objects are inherently reference by nature that the caller variable would now be of type Derived. The only way to get the caller to be type Derived is to pass a reference object by ref like so
private void CallChangeTypeByReference()
{
var b = new Base();
TryChangeTypeByReference(ref b);
Console.WriteLine(b.GetType().ToString());
}
private void TryChangeTypeByReference(ref Base instance)
{
var d = new Derived();
instance = d;
}
Further more, I feel like it's common knowledge that passing in an object to a method, editing props, and passing that object down the stack will keep the changes made down the stack. This makes sense as the object is a reference object.
What causes an object to permanently change type down the stack, only if it's passed in by reference?
You have a great many confused and false beliefs. Let's fix that.
Consider covariance where you can substitute a derived type for a base class.
That is not covariance. That is assignment compatibility. An Apple is assignment compatible with a variable of type Fruit because you can assign an Apple to such a variable. Again, that is not covariance. Covariance is the fact that a transformation on a type preserves the assignment compatibility relationship. A sequence of apples can be used somewhere that a sequence of fruit is needed because apples are a kind of fruit. That is covariance. The mapping "apple --> sequence of apples, fruit --> sequence of fruit" is a covariant mapping.
Moving on.
Passing in typeof(Base) to this method and setting that variable to the derived type is possible.
You are confusing types with instances. You do not pass typeof(Base) to this method; you pass a reference to Base to this instance. typeof(Base) is of type System.Type.
As you correctly note, formal parameters are variables. A formal parameter is a new variable, and it is initialized to the actual parameter aka argument.
However, when checking the type from the caller of the above function, the instance will still be of type Base
Correct. The argument is of type Base. You copy that to a variable, and then you reassign the variable. This is no different than saying:
Base x = new Base();
Base y = x;
y = new Derived();
And now x is still Base and y is Derived. You assigned the same variable twice; the second assignment wins. This is no different than if you said a = 1; b = a; b = 2; -- you would not expect a to be 2 afterwards just because you said b = a in the past.
I would assume since objects are inherently reference by nature that the caller variable would now be of type Derived.
That assumption is wrong. Again, you have made two assignments to the same variable, and you have two variables, one in the caller, and one in the callee. Variables contain values; references to objects are values.
The only way to get the caller to be type Derived is to pass a reference object by ref like so
Now we're getting to the crux of the problem.
The correct way to think about this is that ref makes an alias to a variable. A normal formal parameter is a new variable. A ref formal parameter makes the variable in the formal parameter an alias to the variable at the call site. So now you have one variable but it has two names, because the name of the formal parameter is an alias for the variable at the call. This is the same as:
Base x = new Base();
ref Base y = ref x; // x and y are now two names for the same variable
y = new Derived(); // this assigns both x and y because there is only one variable, with two names
Further more, I feel like it's common knowledge that passing in an object to a method, editing props, and passing that object down the stack will keep the changes made down the stack. This makes sense as the object is a reference object.
Correct.
The mistake you are making here is very common. It was a bad idea for the C# design team to name the variable aliasing feature "ref" because this causes confusion. A reference to a variable makes an alias; it gives another name to a variable. A reference to an object is a token that represents a specific object with a specific identity. When you mix the two it gets confusing.
The normal thing to do is to not pass variables by ref particularly if they contain references.
What causes an object to permanently change type down the stack, only if it's passed in by reference?
Now we have the most fundamental confusion. You have confused objects with variables. An object never changes its type, ever! An apple is an object, and an apple is now and forever an apple. An apple never becomes any other kind of fruit.
Stop thinking that variables are objects, right now. Your life will get so much better. Internalize these rules:
variables are storage locations that store values
references to objects are values
objects have a type that never changes
ref gives a new name to an existing variable
assigning to a variable changes its value
Now if we ask your question again using correct terminology, the confusion disappears immediately:
What causes the value of a variable to change its type down the stack, only if it's passed in by ref?
The answer is now very clear:
A variable passed by ref is an alias to another variable, so changing the value of the parameter is the same as changing the value of the variable at the call site
Assigning an object reference to a variable changes the value of that variable
An object has a particular type
If we don't pass by ref but instead pass normally:
A value passed normally is copied to a new variable, the formal parameter
We now have two variables with no connection; changing one of them does not change the other.
If that's still not clear, start drawing boxes, circles and arrows on a whiteboard, where objects are circles, variables are boxes, and object references are arrows from variables to objects. Making an alias via ref gives a new name to an existing circle; calling without ref makes a second circle and copies the arrow. It'll all make sense then.
This is not an issue with inheritance and polymorphism, what you're seeing is the difference between pass-by-value and pass-by-reference.
private void TryChangeType(Base instance)
The preceding method's instance parameter will be a copy of the caller's Base reference. You can change the object that is referenced and those changes will be visible to the caller because both the caller the callee both reference the same object. But, any changes to the reference itself (such as pointing it to a new object) will not affect the caller's reference. This is why it works as expected when you pass by reference.
When you call TryChangeType() you are passing a copy of the reference to "b" into "instance". Any changes to members of "instance" are made in the same memory space still referenced by "b" in your calling method. However, the command "instance = d" reassigns the value of the memory addressed by "instance". "b" and "instance no longer point to the same memory. When you return to CallChangeType, "b" still references the original space and hence Type.
TryChangeTypeByReference passes the a reference to where "b"'s pointer value is actually stored. Reassigning "instance" now changes the address that "b" is actually pointing to.
We know that class are reference types, so in general when we are passing a type, we are passing a reference but there's a difference between passing just b and ref b, which can be understood as:
In first case 1 it is passing reference by value, which means creating a separate pointer internally to the memory location, now when base class object is assigned to the derived class object, it starts pointing to another object in the memory and when that method returns, only the original pointer remains, which provides the same instance as Base class, when the new pointer created is off for garbage collection
However when object is passed as ref, this is passing reference to a reference in memory, which is like pointer to a pointer, like double pointer in C or C++, which when changes actually changes the original memory allocation and thus you see the difference
For first one to show the same result value has to be returned from the method and old object shall start pointing to the new derived object
Following is the modification to your program to get expected result in case 1:
private Base TryChangeType(Base instance)
{
var d = new Derived();
instance = d;
Console.WriteLine(instance.GetType().ToString());
return instance;
}
private void CallChangeType()
{
var b = new Base();
b = TryChangeType(b);
Console.WriteLine(b.GetType().ToString());
}
Following is the pictorial reference of both the cases:
When you do not pass by reference, a copy of the base class object is passed inside the function, and this copy is changed inside the TryChangeType function. When you print the type of the instance of the base class it is still the of the type "Base" because the copy of the instance was changed to "Derived" class.
When you pass by referece, the address of the instance i.e. the instace itself will be passed to the function. So any changes made to the instance inside the function is permanent.

I don't understand why we need the 'new' keyword

I am new to C#, from a C++ background. In C++ you can do this:
class MyClass{
....
};
int main()
{
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
}
Whereas, in C#:
class Program
{
static void Main(string[] args)
{
Car x;
x.i = 2;
x.j = 3;
Console.WriteLine(x.i);
Console.ReadLine();
}
}
class Car
{
public int i;
public int j;
}
you can't do this. I wonder why Car x won't do its work.
There are a lot of misconceptions here, both in the question itself and in the several answers.
Let me begin by examining the premise of the question. The question is "why do we need the new keyword in C#?" The motivation for the question is this fragment of C++:
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
I criticize this question on two grounds.
First, these do not do the same thing in C++, so the question is based on a faulty understanding of the C++ language. It is very important to understand the difference between these two things in C++, so if you do not understand very clearly what the difference is, find a mentor who can teach you how to know what the difference is, and when to use each.
Second, the question presupposes -- incorrectly -- that those two syntaxes do the same thing in C++, and then, oddly, asks "why do we need new in C#?" Surely the right question to ask given this -- again, false -- presupposition is "why do we need new in C++?" If those two syntaxes do the same thing -- which they do not -- then why have two syntaxes in the first place?
So the question is both based on a false premise, and the question about C# does not actually follow from the -- misunderstood -- design of C++.
This is a mess. Let's throw out this question and ask some better questions. And let's ask the question about C# qua C#, and not in the context of the design decisions of C++.
What does the new X operator do in C#, where X is a class or struct type? (Let's ignore delegates and arrays for the purposes of this discussion.)
The new operator:
Causes a new instance of the given type to be allocated; new instances have all their fields initialized to default values.
Causes a constructor of the given type to be executed.
Produces a reference to the allocated object, if the object is a reference type, or the value itself if the object is a value type.
All right, I can already hear the objections from C# programmers out there, so let's dismiss them.
Objection: no new storage is allocated if the type is a value type, I hear you say. Well, the C# specification disagrees with you. When you say
S s = new S(123);
for some struct type S, the spec says that new temporary storage is allocated on the short-term pool, initialized to its default values, the constructor runs with this set to refer to the temp storage, and then the resulting object is copied to s. However, the compiler is permitted to use a copy-elision optimization provided that it can prove that it is impossible for the optimization to become observed in a safe program. (Exercise: work out under what circumstances a copy elision cannot be performed; give an example of a program that would have different behaviours if elision was or was not used.)
Objection: a valid instance of a value type can be produced using default(S); no constructor is called, I hear you say. That's correct. I didn't say that new is the only way to create an instance of a value type.
In fact, for a value type new S() and default(S) are the same thing.
Objection: Is a constructor really executed for situations like new S(), if not present in the source code in C# 6, I hear you say. This is an "if a tree falls in the forest and no one hears it, does it make a sound?" question. Is there a difference between a call to a constructor that does nothing, and no call at all? This is not an interesting question. The compiler is free to elide calls that it knows do nothing.
Suppose we have a variable of value type. Must we initialize the variable with an instance produced by new?
No. Variables which are automatically initialized, such as fields and array elements, will be initialized to the default value -- that is, the value of the struct where all the fields are themselves their default values.
Formal parameters will be initialized with the argument, obviously.
Local variables of value type are required to be definitely assigned with something before the fields are read, but it need not be a new expression.
So effectively, variables of value type are automatically initialized with the equivalent of default(S), unless they are locals?
Yes.
Why not do the same for locals?
Use of an uninitialized local is strongly associated with buggy code. The C# language disallows this because doing so finds bugs.
Suppose we have a variable of reference type. Must we initialize S with an instance produced by new?
No. Automatic-initialization variables will be initialized with null. Locals can be initialized with any reference, including null, and must be definitely assigned before being read.
So effectively, variables of reference type are automatically initialized with null, unless they are locals?
Yes.
Why not do the same for locals?
Same reason. A likely bug.
Why not automatically initialize variables of reference type by calling the default constructor automatically? That is, why not make R r; the same as R r = new R();?
Well, first of all, many types do not have a default constructor, or for that matter, any accessible constructor at all. Second, it seems weird to have one rule for an uninitialized local or field, another rule for a formal, and yet another rule for an array element. Third, the existing rule is very simple: a variable must be initialized to a value; that value can be anything you like; why is the assumption that a new instance is desired warranted? It would be bizarre if this
R r;
if (x) r = M(); else r = N();
caused a constructor to run to initialize r.
Leaving aside the semantics of the new operator, why is it necessary syntactically to have such an operator?
It's not. There are any number of alternative syntaxes that could be grammatical. The most obvious would be to simply eliminate the new entirely. If we have a class C with a constructor C(int) then we could simply say C(123) instead of new C(123). Or we could use a syntax like C.construct(123) or some such thing. There are any number of ways to do this without the new operator.
So why have it?
First, C# was designed to be immediately familiar to users of C++, Java, JavaScript, and other languages that use new to indicate new storage is being initialized for an object.
Second, the right level of syntactic redundancy is highly desirable. Object creation is special; we wish to call out when it happens with its own operator.
In C# you can do the similar thing:
// please notice "struct"
struct MyStruct {
....
}
MyStruct sample1; // this will create object on stack
MyStruct sample2 = new MyStruct(); // this does the same thing
Recall that primitives like int, double, and bool are also of type struct, so even though it's conventional to write
int i;
we may also write
int i = new int();
unlike C++, C# doesn't use pointers (in the safe mode) to instances,
however C# has class and struct declarations:
class: you have reference to instance,
memory is allocated on heap,
new is mandatory; similar to MyClass* in C++
struct: you have value,
memory is (usually) allocated on stack,
new is optional; similar to MyClass in C++
In your particular case you can just turn Car into struct
struct Car
{
public int i;
public int j;
}
and so the fragment
Car x; // since Car is struct, new is optional now
x.i = 2;
x.j = 3;
will be correct
In C#, class type objects are always allocated on the heap, i.e. variables of such types are always references ("pointers"). Just declaring a variable of such a type does not cause the allocation of an object. Allocating a class object on the stack like it's common to do in C++ isn't (in general) an option in C#.
Local variables of any type that have not been assigned to are considered uninitialized, and they cannot be read until they have been assigned to. This is a design choice (another way would have been to assign default(T) to every variable at declaration time) which seems like a good idea because it should protect you from some programming errors.
It's similar to how in C++ it wouldn't make sense to say SomeClass *object; and never assign anything to it.
Because in C# all class type variables are pointers, allocating an empty object when the variable is declared would lead to inefficient code when you actually only want to assign a value to the variable later, for instance in situations like this:
// Needs to be declared here to be available outside of `try`
Foo f;
try { f = GetFoo(); }
catch (SomeException) { return null; }
f.Bar();
Or
Foo f;
if (bar)
f = GetFoo();
else
f = GetDifferentFoo();
ignoring the stack vs heap side of things:
because C# made the bad decision to copy C++ when they should have just made the syntax
Car car = Car()
(or something similar). Having 'new' is superfluous.
When you use referenced types then in this statement
Car c = new Car();
there are created two entities: a reference named c to an object of type Car in the stack and the object of type Car itself in the heap.
If you will just write
Car c;
then you create an uninitialized reference (provided that c is a local variable) that points to nowhere.
In fact it is equivalent to C++ code where instead of references there are used pointers.
For example
Car *c = new Car();
or just
Car *c;
The difference between C++ and C# is that C++ can create instances of classes in the stack like
Car c;
In C# this means creating a reference of type Car that as I said points nowhere.
From the microsoft programming guide:
At run time, when you declare a variable of a reference type, the variable contains the value null until you explicitly create an instance of the object by using the new operator, or assign it an object that has been created elsewhere by using new
A class is a reference type. When an object of the class is created, the variable to which the object is assigned holds only a reference to that memory. When the object reference is assigned to a new variable, the new variable refers to the original object. Changes made through one variable are reflected in the other variable because they both refer to the same data.
A struct is a value type. When a struct is created, the variable to which the struct is assigned holds the struct's actual data. When the struct is assigned to a new variable, it is copied. The new variable and the original variable therefore contain two separate copies of the same data. Changes made to one copy do not affect the other copy.
I think in your C# example your effectively trying to assign values to a null pointer. In c++ translation this would look like:
Car* x = null;
x->i = 2;
x->j = 3;
This would obviously compile but crash.

What exactly is new [] doing in a class vs struct?

MyClass[] CLASS = new MyClass[5];
int[] STRUCT = new int[5];
What exactly is new [] doing for a class vs a struct. Apparently the struct has some overloaded static index that causes it to run the default constructor of the struct. However the new [] for a class appears to do nothing but make space to initialize an instance of a class. How do I overload the static behavior of the class to also run the default constructor. I know how to use for loops and other methods of accomplishing this. My question is very specific to what is going on underneath the new []. I understand that a struct needs a default value. But doesn't a non-nullable class also need a default value which is why it gives an error when you try to use it? Or is this telling me that all classes are actually nullable?
No, the default constructor of struct isn't run. What happens instead is that in the struct case, the memory is initialized to zero. What this means depends on the data type. E.g., a reference field (like a class or a string) becomes null, numeric fields become 0, boolean fields become false, etc.
This differs very much from how classes work. This is why with the initialization of the class array, you see null values. Basically, it comes down to this. An "empty" class variable (or array in your case) becomes null. However, when you have an "empty" struct, you already have something valid. However, it is initialized as empty.
The easiest way to see this is when you e.g. have an int field in a class. This works basically the same way. When you add an int field to a class, you are not required to initialize it. When you don't initialize it, it gets the value 0 by default. struct's work the same way in this regard.
See http://msdn.microsoft.com/en-us/library/aa664471.aspx for a bit more information.
A few more things to note (answer to the question in the comments):
Classes are always nullable. This means that the only way to initialize the array is to create a loop and initialize a new instance for every item in the array;
The default constructor doesn't apply to structs, because they aren't allowed to have a default constructor. If you try to compile the following code snippet, you will get a compilation error telling you this:
struct MyStruct
{
public MyStruct()
{
}
}

Is there a way I can prevent struct from being insantiated or can I have a class that will be copied?

Ok this is more curiosity than practical requirement.
Let's say I have this class:
public sealed class Entity
{
int value;
Entity()
{
}
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
I don't want this class to be instantiated outside the class and this works. But here Entitys will be referenced and not copied like value types. This class being so small I want it to behave likes value types. Also if I compare two instances of Entitys like e1 == e2 it's going to give reference equality (ok I can overload == but that's more work). So I would make it a struct:
public struct Entity
{
int value;
public static implicit operator Entity(int x)
{
return new Entity { value = x };
}
}
But now someone can do new Entity() easily.
My question is there a way I can have a type (struct/class) that
1) will be copied every time the value is accessed, so that
Entity e1 = 1;
Entity e2 = e1;
ReferenceEquals(e1, e2); // prints false, just like any value type
2) has value semantics when comparing for equality
Entity e1 = 1;
Entity e2 = 1;
e1.Equals(e2); // prints true
// this is easy though by overriding default equality.
3) is not instantiable outside the scope of the type:
Entity e = new Entity(); // should not compile
Basically close to an enum's behaviour. I can certainly live without it, just learning.
Its not possible to do re-define or try to hide the default constructor for a struct in C# because the C# compiler routinely comples code based on the assumption that the parameterless constructor does nothing. See Why can't I define a default constructor for a struct in .NET?.
Your options are either
Use a class
Ensure that the default value for your struct has some meaning (e.g. in your case the int will be initialised to 0) - this is what enums do (recall that enums are initialised to 0).
My recommendation would be to use a class until you identify that there is a performance problem.
A type has no say over what can happen to storage locations of that type. Storage locations of any type can come into existence, or have their contents overwritten with the content of other locations of the same type, without the type itself having any say in the matter. A storage location of a reference type holds a class reference; when it comes into existence, it will be a null reference. The only way a storage location of a reference type will hold anything other than a null reference is if an instance of that type is created and--outside some scenarios involving Full Trust Code and reflection--a reference type will have full control over the circumstances where that can occur.
By contrast, a storage location of a value type is an instance of that type. Consequently, there is no way a structure can control how one comes into existence. What a structure can control--to a limited extent--is what values its fields can contain. In limited trust scenarios, a private struct field can only assume a non-default value if the code for the struct writes that value to some instance of the struct (if the value gets written to any instance of the struct, even limited-trust code with access to that instance may be able to use multi-threaded overlapped reads and writes to produce a struct with a combination of field values which the struct itself would never create itself).
I would suggest creating a struct with a single private field of a class type. You cannot prevent code from creating a default(Entity), but that doesn't mean you have to allow code to do anything useful with one. Its Equals method should work well enough to return true when comparing to another default instance, and false when compared with anything else, and GetHashCode should return the same value for all default instances, but any other methods could throw an exception.
Make all of the constructors (besides the static constructor) private for the class Entity. protected would work too, but since its sealed, that doesn't make a lot of sense.

How to pass this by ref in C#?

In a class (ClassA) of mine I want to create a related instance of another class (ClassB) providing it with a reference to the object who has initiated it's creation. So I've provided ClassB with a construcror taking a (ref ClassB parent) argument. But in ClassA I can't just call var child = new ClassB(ref this). How to implement this?
The ref keyword causes Pass by Reference semantics - that is, if the variable is re-assigned in the called function, it will re-assign the variable in the caller as well.
Obviously, this only works if a variable2 (which can be re-assigne to) is directly passed as the argument and will not work if an arbitrary expression is passed. In this case, this is not a variable, rather a special expression which cannot be re-assigned, and so cannot be used.
As such, this would work: (But please see other answers and keep reading as to why this is likely not required and/or just silly.)
var me = this;
var other = new ClassB(ref me);
However, Pass by reference should not be confused with Pass by Object [Sharing]1 semantics. Pass by Object means that if an object is passed, that object is passed: the object is not copied/cloned/duplicated. If the object is mutated then the object is mutated. All Reference Types have Pass by Object semantics in C# unless either ref or out are used. (The class keyword declares a new reference type, as in the case in the post).
On the other hand, Value Types (e.g. struct), including int and Guid and KeyValuePair<K,V>, have Pass by Value semantics - in this case a copy is made and thus, if the value is modified, only the value struct (which is a copy) changes.
Happy coding
1 Underneath C#/.NET achieves Pass by Object by passing a reference to an object by Value. However, the rules above correctly describe the observable semantics and effects.
2 Unlike C#, which only allows variables to be used with ref, VB.NET allows Properties to be used. The VB.NET compiler automatically creates a temporary variable and implicit reading/writing instructions of the property during compilation.
ref refers to variable references, not object references.
If you just want to pass a reference to an object, the ref keyword isn't necessary. Objects are already reference types, so it's their references being passed by value. The objects themselves aren't copied.
So, you neither need the ref keyword in the constructor nor in the instantiation:
public ClassB(ClassA parent)
{
}
var child = new ClassB(this);
You don't need to pass by ref in this case. If you are passing ClassB(this), it will be passed by reference and not by value anyway.
Any changes made to the classA instance passed into classB's constructor will be applied to class A as well.
Do you really need the ref keyword? All the types are basically passed by reference, so if you have ClassB take ClassA as constructor argument, just pass new ClassB(this), no need to use ref.
You can't change the this pointer, which is something that the ref keyword allows. Why can't you declare ClassB like this?
ClassA m_classA;
public ClassB(ClassA classA)
{
m_classA = classA;
}

Categories