Maybe this is real simple or breaking all the rules or maybe I just dont know what its called so I cant find it.
Anyway, I want to be able to replace an entire object on the heap. I've added a small code sample to show what I want to do, and a way of doing it, but I just want to know if there is a more elegant way?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BasicObjectTest
{
class Program
{
static void Main(string[] args)
{
List<Test> testList = new List<Test>
{
new Test {Value=1,NiceString="First" },
new Test {Value=2,NiceString="Second" },
new Test {Value=3,NiceString="Third" }
};
var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
EasyWay(testList, replacementTestClass);
var correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
Console.WriteLine(correctTestClass.NiceString); //Expecting "Forth"
Console.ReadLine();
HardWay(testList, replacementTestClass);
correctTestClass = testList.FirstOrDefault(x => x.Value == 2);
Console.WriteLine(correctTestClass.NiceString);
Console.ReadLine();
}
private static void HardWay(List<Test> testList, Test replacementTestClass)
{
//This will work!
var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
CopyPropertiesUsingPropertyInfo(secondTestClass, replacementTestClass);
}
private static void CopyPropertiesUsingPropertyInfo(Test secondTestClass, Test replacementTestClass)
{
foreach(var pi in secondTestClass.GetType().GetProperties())
{
pi.SetValue(secondTestClass, pi.GetValue(replacementTestClass, null));
}
}
private static void EasyWay(List<Test> testList, Test replacementTestClass)
{
//This wont work, but I want it to!
var secondTestClass = testList.FirstOrDefault(x => x.Value == 2);
secondTestClass = replacementTestClass;
}
}
}
and my Test object
class Test
{
public int Value { get; set; }
public string NiceString { get; set; }
}
There must be a more elegant way of doing this?
I know why the first alternative does not work: I just change the object reference for that variable.
Update:
Using this thinking I understood it for a long time I tested this now thinking it would work, but the test fails. Why? Didnt I replace the object so that every object using it should use the new object? See complete code below
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var main = new Main { Property = 1 };
var dependent = new Dependent(main);
void ChangeRef(ref Main Oldmain, Main newMain)
{
Oldmain = newMain;
}
ChangeRef(ref main, new Main { Property = 5 });
Assert.AreEqual(5,dependent.Main.Property);
}
}
public class Main
{
public int Property { get; set; }
}
public class Dependent
{
public Dependent(Main main)
{
Main = main;
}
public Main Main { get; set; }
}
There must be a more elegant way of doing this?
There is one basic thing you're missing. When you search for the object in the list, and one is found, you get back a copy of the reference pointing to that object. This means that when you alter it, you're only altering the copy. The original reference in the list is still pointing to that same old object instance.
but what if I didnt have a list. I just had the object reference in a
variable?
Then you could use the ref keyword to pass the reference type by reference:
public static void Main(string[] args)
{
var test = new Test { Value = 1, NiceString = "First" };
var newTest = new Test { Value = 2, NiceString = "AlteredTest!" };
UpdateTest(ref test, newTest);
Console.WriteLine(test.NiceString); // "AlteredTest!"
}
public static void UpdateTest(ref Test originalTest, Test other)
{
originalTest = other;
}
An alternative way to approach this is with the proverbial "extra level of indirection".
Instead of storing the objects in the list, you store wrapper objects instead. The wrapper object provides an "Item" field which points to the actual object. Then you can update the "Item" field to point it at the new object.
A simple generic wrapper class could look like this:
class Wrapper<T>
{
public T Item;
public Wrapper(T item)
{
Item = item;
}
public static implicit operator Wrapper<T>(T item)
{
return new Wrapper<T>(item);
}
}
Then you could use it like so:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication2
{
class Test
{
public int Value { get; set; }
public string NiceString { get; set; }
}
class Wrapper<T>
{
public T Item;
public Wrapper(T item)
{
Item = item;
}
public static implicit operator Wrapper<T>(T item)
{
return new Wrapper<T>(item);
}
}
class Program
{
static void Main(string[] args)
{
var testList = new List<Wrapper<Test>>
{
new Test {Value = 1, NiceString = "First"},
new Test {Value = 2, NiceString = "Second"},
new Test {Value = 3, NiceString = "Third"}
};
var replacementTestClass = new Test { Value = 2, NiceString = "NEW" };
EasyWay(testList, replacementTestClass);
var correctTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
Console.WriteLine(correctTestClass.Item.NiceString); //Expecting "New"
Console.ReadLine();
}
private static void EasyWay(List<Wrapper<Test>> testList, Test replacementTestClass)
{
var secondTestClass = testList.FirstOrDefault(x => x.Item.Value == 2);
secondTestClass.Item = replacementTestClass;
}
}
}
Related
I want to check specific fields of a class for content. If there is no value, it should give a message. The current example is working. But I am looking for a way to to the same without Reflection.
using System;
using System.Reflection;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
TableDescription table1 = new TableDescription { BUYER_AID = 0, DESCRIPTION_LONG = 3, EAN = 2, SUPPLIER = 17};
TableDescription table2 = new TableDescription();
string [] members = new string[] { "BUYER_AID", "DESCRIPTION_LONG", "EAN" };
CheckAndSetValue(table1, table2, members);
}
static void CheckAndSetValue(TableDescription t1, TableDescription t2, string[] list)
{
foreach (string name in list)
{
Type type = typeof(TableDescription);
FieldInfo typeinfo = type.GetField(name);
short value = Convert.ToInt16(typeinfo.GetValue(t1));
if (0 != value)
{
typeinfo.SetValue(t2, value);
}
else
{
Console.WriteLine($"Value for {name} is missing!");
}
}
}
}
public class TableDescription
{
public short BUYER_AID = 0;
public short DESCRIPTION_LONG = 0;
public short EAN = 0;
public short SUPPLIER = 0;
}
}
Is there a way to to it something like:
var[] members = new var[]{TableDescription.BUYER_AID, TableDescription.DESCRIPTION_LONG, TableDescription.EAN};
I am looking for a solution to work without stings. Working with Strings will make trouble with refactoring and on error it will crash during runtime.
Hello I am doing some tests in C# with nesting properties which return objects, but I am getting an object reference exception.
I want to be able to access the arrays in nested Properties, but in the current context I can see that I'm not instancing any new objects inside the properties.
This is where the basic question comes up... Where do I declare a 'new' object instance in the middle of all this? Do I even need to declare and new object reference inside the 'foo' class or 'bar' class?
namespace CustomProperties_TEST
{
class Program
{
public foo[] Blatherskite { get; set; }
static void Main(string[] args)
{
Program myProgram = new Program();
myProgram.Blatherskite[0].CustomProperty1[0].CustomProperty2 = 999999999;
myProgram.Blatherskite[1].CustomProperty1[0].CustomProperty2 = 999999999;
foreach (var item in myProgram.Blatherskite)
{
Console.WriteLine(item.CustomProperty1[0].CustomProperty2);
}
}
}
class foo
{
private bar[] customevariable1;
public bar[] CustomProperty1
{
get { return customevariable1; }
set { customevariable1 = value; }
}
}
class bar
{
private int customintvariable2;
public int CustomProperty2
{
get { return customintvariable2; }
set { customintvariable2 = value; }
}
}
}
You would want to do something like the following, since arrays are initialized to null by default.
static void Main(string[] args)
{
Program myProgram = new Program();
// This is your missing initialization
myProgram.Blatherskite = new foo[2] {
new foo{CustomProperty1 = new bar[2]{new bar{CustomProperty2 = 1},new bar{CustomProperty2 = 2}}}
, new foo{CustomProperty1 = new bar[2]{new bar{CustomProperty2 = 3},new bar{CustomProperty2 = 4}}}};
myProgram.Blatherskite[0].CustomProperty1[0].CustomProperty2 = 999999999;
myProgram.Blatherskite[1].CustomProperty1[0].CustomProperty2 = 999999999;
foreach (var item in myProgram.Blatherskite)
{
Console.WriteLine(item.CustomProperty1[0].CustomProperty2);
}
}
Using arrays means you'll have to set their size. If you would want more flexibility, use a List, and then you can simply add items to it.
I have a Model object that contains a list of node. These nodes contain a signature.
I would like to have a property with a getter returning an array of signatures. I have trouble to instantiate the array and I'm not sure if I should use an array/list/enumerable or something else.
How would you achieve this?
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var m = new Model();
Console.WriteLine(m.Signatures.ToString());
Console.ReadLine();
}
}
public class Model
{
public List<Node> Nodes { get; set; }
public int[][] Signatures
{
get
{
return Nodes.Select(x => x.Signature) as int[][];
}
}
public Model()
{
Nodes = new List<Node>();
Nodes.Add(new Node { Signature = new[] { 1,1,0,0,0 } });
Nodes.Add(new Node { Signature = new[] { 1,1,0,0,1 } });
}
}
public class Node
{
public int[] Signature { get; set; }
}
}
Use ToArray()
return Nodes.Select(x => x.Signature).ToArray();
And something like this to output it correctly:
Array.ForEach(m.Signatures, x=>Console.WriteLine(string.Join(",", x)));
In your Signatures property you try to use the as operator to convert the type into int[][]. The Select method however returns an IEnumerable<int[]> which is not an array. Use ToArray to create the array:
public int[][] Signatures
{
get
{
return Nodes.Select(x => x.Signature).ToArray();
}
}
I used to compare lists like this, but it returns false in a test:
Assert.IsTrue(expected.SequenceEquals(actual));
And tried converting to json and it worked:
Assert.AreEqual(expected.ToJson(), actual.ToJson());
Values seems to be equal, what could be different? How to find out what is different in the lists?
Updated:
My class:
public class Department
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
If MyClass implements IEquatable<MyClass>, then try this:
expected.Sort();
actual.Sort();
if (Enumerable.SequenceEqual(actual, expected)) { ... }
If it does not implement IEquatable then you could expect strange behavior, since the object references will be compared in the two lists, and not their fields:
using System;
using System.Collections.Generic;
using System.Linq;
public class MyClassA
{
private int i;
public MyClassA(int i) { this.i = i; }
}
public class MyClassB : IEquatable<MyClassB>
{
private int i;
public MyClassB(int i) { this.i = i; }
public bool Equals(MyClassB other) { return this.i == other.i; }
}
public class Program
{
public static void Main()
{
var actual1 = new List<MyClassA>() { new MyClassA(1), new MyClassA(2), new MyClassA(3) };
var expected1 = new List<MyClassA>() { new MyClassA(1), new MyClassA(2), new MyClassA(3) };
Console.WriteLine(Enumerable.SequenceEqual(actual1, expected1));
var a1 = new MyClassA(1);
var a2 = new MyClassA(2);
var a3 = new MyClassA(3);
var actual2 = new List<MyClassA>() { a1, a2, a3 };
var expected2 = new List<MyClassA>() { a1, a2, a3 };
Console.WriteLine(Enumerable.SequenceEqual(actual2, expected2));
var actual3 = new List<MyClassB>() { new MyClassB(1), new MyClassB(2), new MyClassB(3) };
var expected3 = new List<MyClassB>() { new MyClassB(1), new MyClassB(2), new MyClassB(3) };
Console.WriteLine(Enumerable.SequenceEqual(actual3, expected3));
var actual4 = new List<MyClassB>() { new MyClassB(1), new MyClassB(2), new MyClassB(3) };
var expected4 = new List<MyClassB>() { new MyClassB(3), new MyClassB(2), new MyClassB(1) };
Console.WriteLine(Enumerable.SequenceEqual(actual4, expected4));
}
}
Output:
False
True
True
False
using System.Linq;
Enumerable.SequenceEqual(a, b);
// or SequenceEqual(a, b, StringComparer.OrdinalIgnoreCase)
See MSDN, also this question.
Perhaps you can use IEnumerable.ExceptOf
http://msdn.microsoft.com/en-us/library/bb300779.aspx
Our perhaps you can use an HashSet and there the intersect method.
http://msdn.microsoft.com/en-us/library/bb293080.aspx
I tend to find the HashSet<T> collection fit for this kind of purpose, cast your collections into HashSet<T> then call SetEquals
I saw an example on MSDN where it would let you specify the default value if nothing is returned. See below:
List<int> months = new List<int> { };
int firstMonth2 = months.DefaultIfEmpty(1).First();
Is it possible to use this functionality with an object? Example:
class object
{
int id;
string name;
}
code:
List<myObjec> objs = new List<myObjec> {};
string defaultName = objs.DefaultIfEmpty(/*something to define object in here*/).name;
UPDATE:
I was thinking I could do something like this:
List<myObjec> objs = new List<myObjec> {};
string defaultName = objs.DefaultIfEmpty(new myObjec(-1,"test")).name;
But haven't been able to. It should be noted that I am actually trying to use this method on an object defined in my DBML using LINQ-To-SQL. Not sure if that makes a difference in this case or not.
You need to pass an instantiated class as a parameter of the DefaultIfEmpty.
class Program
{
static void Main(string[] args)
{
var lTest = new List<Test>();
var s = lTest.DefaultIfEmpty(new Test() { i = 1, name = "testing" }).First().name;
Console.WriteLine(s);
Console.ReadLine();
}
}
public class Test
{
public int i { get; set; }
public string name { get; set; }
}
To add to it and make it a bit more elegant (IMO) add a default constructor:
class Program
{
static void Main(string[] args)
{
var lTest = new List<Test>();
var s = lTest.DefaultIfEmpty(new Test()).First().name;
Console.WriteLine(s);
Console.ReadLine();
}
}
public class Test
{
public int i { get; set; }
public string name { get; set; }
public Test() { i = 2; name = "testing2"; }
}
As per the MSDN page on this Extension Method you can do what you want:
http://msdn.microsoft.com/en-us/library/bb355419.aspx
Check the sample on this page for an example on how to use this with an object.
i must admit i am not too sure i understand your question, but i'll try to suggest using double question mark if the returned object might be null. Like so:
myList.FirstOrDefault() ?? new myObject();
You can create a default Object Like this:
Object o_Obj_Default = new Object();
o_Obj_Default.id = 3;
o_Obj_Default.name = "C";
And add it to your default value :
string defaultName = objs.DefaultIfEmpty(o_Obj_Default).First().name;
If your list "objs" is empty, the result will be "C"