I have a Vector class, and I was testing the following unit test (using nUnit).
1 Vector test1 = new Vector(new double[] { 6, 3, 4, 5 });
2 Vector test2 = test1;
3 Assert.AreEqual(test1, test2, "Reference test");
4 test2 = new Vector(new double[] { 3, 3, 4, 5 });
5 Assert.AreEqual(test1, test2, "Reference test");
The first test in line 3 passes, but the second test in line 5 fails. Shouldn't test2 also point to the same memory as test1, since I did the assignment statement in line 2? My Vector is defined as a class, so it is a reference type. On the other hand, the following tests pass:
1 Vector test1 = new Vector(new double[] { 6, 3, 4, 5 });
2 Vector test2 = test1;
3 Assert.AreEqual(test1, test2, "Reference test");
4 test2[1] = 4;
5 Assert.AreEqual(test1, test2, "Reference test");
Does that mean that, when I use the new operator to define a new object, old assignments are no longer valid? Any other (or correct - if I am wrong) explanation?
The line
test2 = new Vector(new double[] { 3, 3, 4, 5 });
creates a new instance of Vector on the heap and assigns its address to the test2 variable. test2 will point to a new, completely distinct object after that.
In contrast, the line
test2[1] = 4;
does not change the test2 variable itself (which is a reference to some object on the heap). Rather, it's changing the object it points to. test2 still refers to the same location on the heap.
To summarize, in the former, you are changing the reference while in the latter, you are altering the referent.
When you assign a variable like:
test2 = new Vector(new double[] { 3, 3, 4, 5 });
You are changing the value of test2 to be the new reference returned by the right hand side of the assignment operator. Of course the reference returned here is different than the one in test1, because it is a separate case of the constructor being called, and the references could not possibly be the same, as the Vector is being constructed with different arguments.
Yes, when you use the new operator to define a new object, old assignments are no longer valid.
Your vector IS a reference type, but when you say test2 = something you are saying "now test2 points to something else".
As an aside, if you want two different Vector objects with the same internal values to be considered equal, you can get that by implementing IEquatable on your Vector class, but that's another question...
Equals compares the values to see if they match, whereas if you want to compare references you need to use ReferenceEquals.
Check out http://msdn.microsoft.com/en-us/library/dd183752.aspx
Vector test1 = new Vector(new double[] { 6, 3, 4, 5 });
Is creating two objects, the vector its self, and the reference to it.
Imagin you have a list of items on a page.
When you use new Vector you effectively write a new line on the page which contains the vector.
Objects
{1,2,3,4,5}
You also have a list of references (Vector test1 = new Vector) which referance the first page, and (test2 = test1)
References
test1-> 1
test2-> 1
when you say 'test2 = new Vector {5,4,3,2,1} you then end up with a new vector object on the first page, and change which vector test2 is referring to.
Objects
{1,2,3,4,5}
{5,4,3,2,1}
References
test1 -> 1
test2 -> 2
In your second example both test1 and test2 are still pointing to the same object, which is why the test passes.
Related
I've been trying to write a program, in which I want to use the intersection of two HashSets. Therefore I wrote the following code (for test purposes):
HashSet<int> test1 = new HashSet<int>() { 1, 3, 5, 7, 9 };
HashSet<int> test2 = new HashSet<int>() { 1, 2, 3, 4, 5, 6};
HashSet<int> intersect = new HashSet<int>();
intersect = test1.Intersect(test2);
Line 5 shows an error (code CS0266) which - that's C#'s suggestion - can be corrected by change the line to:
intersect = (HashSet<int>)test1.Intersect(test2);
But when I run the program, the error appears again. I literally have no clue why, even after searching for an answer.
I want to achieve a intersection in the mathematical sense, so that the result for the variable intersect should be { 1, 3, 5}.
And what I found out - but couldn't test - is, that after using the intersect-method on test1, it changes the list in test1 to the intersection, is that true? If yes, is there any chance to avoid this? In my real program I don't want the variable to change into the intersection.
Should I just make a for-loop with an if-statement, to make my own intersection-method, or does this make the code worse?
As said, I tried to make use of C#'s suggestion, but this doesn't work either.
Because I'm a programming-beginner, I'm not really able to understand the definition of the intersect-method (because of this IEnumerable thing...), so I can't solve the problem using existend methods. And because I think my own method could be very inefficient, I don't to it my own. Furthermore I just want to understand, what's the problem. There are two HashSets, both containing integers, which should be intersected and saved in an extra variable...
Intersect() returns a IEnumerable<T>. You can use IntersectWith(), which modifies the current HashSet<T> object to contain only elements that are present in that object and in the specified collection:
HashSet<int> test1 = new HashSet<int>() { 1, 3, 5, 7, 9 };
HashSet<int> test2 = new HashSet<int>() { 1, 2, 3, 4, 5, 6};
test1.IntersectWith(test2); // we are altering test1 here
// test1 contains now [1, 3, 5]
or use the side-effect free Linq Intersect() to get an IEnumerable<T> and if you want it to be a new HashSet<T> just use a constructor:
HashSet<int> test1 = new HashSet<int>() { 1, 3, 5, 7, 9 };
HashSet<int> test2 = new HashSet<int>() { 1, 2, 3, 4, 5, 6};
HashSet<int> intersect = new HashSet<int>(test1.Intersect(test2));
// intersect contains now [1, 3, 5]
Remarks (from MSDN)
If the collection represented by the other parameter is a HashSet<T> collection with the same equality comparer as the current HashSet<T> object, this method is an O(n) operation. Otherwise, this method is an O(n + m) operation, where n is Count and m is the number of elements in other.
Basically in your case IntersectWith() is going to be more efficient!
Complete demo:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
HashSet<int> test1 = new HashSet<int>() {1, 3, 5, 7, 9};
HashSet<int> test2 = new HashSet<int>() {1, 2, 3, 4, 5, 6};
HashSet<int> intersect = new HashSet<int>(test1.Intersect(test2));
intersect.Dump();
test1.IntersectWith(test2);
test1.Dump();
}
}
Try it Online!
Enumerable.Intersect is a LINQ extension method that works with any kind of IEnumerable<T>. It returns IEnumerable<T>, so not a HashSet<T>. But since you have alrady two sets you want to use HashSet.IntersectWith(more efficient since O(n)) with manipulates the first HashSet<T>:
test1.IntersectWith(test2); // test1 contains now [1, 3, 5]
Why it's not equal? It's the same with CollectionAssert too.
var a = new[] { new[] { 1, 2 }, new[] { 3, 4 } };
var b = new[] { new[] { 1, 2 }, new[] { 3, 4 } };
// if you comment these two lines the test passes
a[0] = a[1];
b[0] = b[1];
Assert.That(a, Is.EqualTo(b));
Gives:
Expected and actual are both <System.Int32[2][]>
Values differ at index [1]
Expected and actual are both <System.Int32[2]>
I'm using nunit 2.6.4.14350 and run from ReSharper test runner in VS .NET 4.5 project.
The same is reproducable for standalone NUnit test runner (2.6.4).
I reported this bug but it's closed as won't fix: https://github.com/nunit/nunit/issues/1209
So you can either use NUnit 3.x or accept that it's just broken in NUnit 2.6.x.
Althoug either a and bare of type Int32[2][] that does not mean they are equal as Equals returns true if the references of your arrays are identical which they are not. What you want is to echeck if their content is the same using a.SequenceEquals(b).
I checked the section of the C# language specification regarding enums, but was unable to explain the output for the following code:
enum en {
a = 1, b = 1, c = 1,
d = 2, e = 2, f = 2,
g = 3, h = 3, i = 3,
j = 4, k = 4, l = 4
}
en[] list = new en[] {
en.a, en.b, en.c,
en.d, en.e, en.f,
en.g, en.h, en.i,
en.j, en.k, en.l
};
foreach (en ele in list) {
Console.WriteLine("{1}: {0}", (int)ele, ele);
}
It outputs:
c: 1
c: 1
c: 1
d: 2
d: 2
d: 2
g: 3
g: 3
g: 3
k: 4
k: 4
k: 4
Now, why would it select the third "1", the first "2" and "3", but the second "4"? Is this undefined behavior, or am I missing something obvious?
This is specifically documented to be undocumented behaviour.
There is probably something in the way the code is written that will end up picking the same thing every time but the documentation of Enum.ToString states this:
If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return.
(my emphasis)
As mentioned in a comment, a different .NET runtime might return different values, but the whole problem with undocumented behaviour is that it is prone to change for no (seemingly) good reason. It could change depending on the weather, the time, the mood of the programmer, or even in a hotfix to the .NET runtime. You cannot rely on undocumented behavior.
Note that in your example, ToString is exactly what you want to look at since you're printing the value, which will in turn convert it to a string.
If you try to do a comparison, all the enum values with the same underlying numerical value is equivalent and you cannot tell which one you stored in a variable in the first place.
In other words, if you do this:
var x = en.a;
there is no way to afterwards deduce that you wrote en.a and not en.b or en.c as they all compare equal, they all have the same underlying value. Well, short of creating a program that reads its own source.
This should be a pretty basic question, but I've been having a little trouble finding a definite answer.
When you have an array of values and you use the .ToArray() method does it create a deep or shallow copy of the array?
No.
You can easily verify this by writing a small program to test.
Not strictly speaking, ICollection.ToArray() creates a new T[] and assigns each element in the original collection to the new array using Array.CopyTo().
Note:
If T is a value type, values are assigned and not references. This will behave as one would expect of a "Deep" copy.
int[] foo = { 1, 2, 3, 4 };
int[] bar = foo.ToArray();
for (int i = 0; i < foo.Length; i++) foo[i] += 10;
Console.WriteLine(string.Join(',', foo)); // 11, 12, 13, 14
Console.WriteLine(string.Join(',', bar)); // 1, 2, 3, 4
I have class with a List<int> member. If I want to clone an instance of this class, do I need a deep copy or the MemberwiseClone() shallow copy is enough?
We need a deep copy if at least one member is a reference to an object, right? Does that mean having List, DateTime, String, MyClass, ..., will need a deep copy?
This entirely depends on how you plan to use the copy.
If you do a shallow copy like
List<int> x = new List<int>() { 1, 2, 3, 4, 5 };
List<int> y = x;
y[2] = 4;
Then x will contain {1, 2, 4, 4, 5 }
If you do a deep copy of the list:
List<int> x = new List<int> { 1, 2, 3, 4, 5 };
List<int> y = new List<int>(x);
y[2] = 4;
Then x will contain { 1, 2, 3, 4, 5 } and y will contain { 1, 2, 4, 4, 5 }
How you plan on using the copy really determines whether you use shallow or deep copies.
If you have List<int> originalList = new List{1, 2}
then doing this:
List<int> newList = new List<int>();
foreach(int i in originalList)
newList.Add(i);
will get you a cloned list.
However, if you tried what I did above with a List generic on some reference type, then you would not succeed. After altering one of the objects in your list, you would see the altered version whether referencing it from the original list or the new list.
Short answer, yes.
Long answer, you need to determine what the copy in your case actually means with respect to the list. Do you need a copy of the contents of the list as well? For value types like ints, its pretty straight forward, if it were reference types though....yo have some questions to ask regarding what you want your list to contain.