I have a situation in my program where I have around 20 fields. All of them are strings. Now I also have an array of strings of size 20. I want to sequentially initialize those fields from the string stored inside this array. I do not want to do this:
field1 = array[0];
field2 = array[1];
....
field20 = array[19];
So I wrote a test program like this but it doesn't work with strings. It's only working with reference types. Is there any way to do it?
public class Program
{
private string name;
private string id;
private void Func()
{
var array = new[] {name, id};
for (int i = 0; i < array.Length; i++)
{
array[i] = "some string";
}
}
public static void Main(string[] args)
{
var p = new Program();
p.Func();
Console.WriteLine(p.name); // prints null
}
}
The assumptions about the behavior are incorrect.
// Create a NEW array with the specified expressions which are evaluated
// immediately (to the current values of the fields)..
var array = new[] {name, id};
// Meaning it is equivalent to this .. note that the field names have
// NOTHING to do with the array object itself.
var array = new[] {(string)null, (string)null};
// Then for each item in the array, assign it a value
// (replacing what was already there anyway)
for (int i = 0; i < array.Length; i++) {
array[i] = "some string";
}
And at the end we end up with an array that looks like ["some string", "some string"]. Again, there is no "connection" with the fields. This issue has naught to do with reference types or not (and strings are also reference types).
The best (and usually correct) way is honestly to do it a different way. Although one could wrap field access/setters and there is always reflection.. If dynamic names are truly required then a Dictionary or similar should probably be used instead
Here is a method using a proxies to wrap the assignment operations. This example uses Actions and "Statement Lambda" syntax.
var setters = new Dictionary<string, Action<Program, string>>() {
{ "name", (p, value) => p.name = value },
{ "id", (p, value) => p.id = value },
};
// For each field setter, assign a value (could use the name as a look-up)
foreach (var setter in setters.Values) {
setter(this, "some string");
}
This works because setter(..) invokes the action defined earlier that actually assigns to the appropriate member. Using such a look-up/proxy (using actions, functions, or more complex types) is a valid approach for some situations, but should probably be avoided when not needed.
This can also be done with Reflection.
var t = this.GetType();
var fieldNames = new [] { "name", "id" };
var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
foreach (var name in fieldNames) {
var fieldInfo = t.GetField(name, bindingFlags);
fieldInfo.SetValue(this, "some string");
}
Reflection should generally be a "method of last resort". It loses static typing information, pushes off many errors until run-time, and carries a performance penalty (that, granted, likely doesn't matter). There are some really nifty things that can be done (especially when Expressions and Annotations are also used) .. but reflection is magic best left until it's really needed.
See if this is what you are trying to achieve:
public class Program
{
private string name;
private string id;
private Dictionary<string, int> mapper = new Dictionary<string, int>();
private String[] array= null;
public Program()
{
mapper.Add("name", 1);
mapper.Add("id", 2);
}
public string Name
{
get { return array[mapper["name"]]; }
}
public string Id
{
get { return array[mapper["id"]]; }
}
private void Func()
{
array = new[] { name, id };
for (int i = 0; i < array.Length; i++)
{
array[i] = "some string";
}
}
public static void Main(string[] args)
{
var p = new Program();
p.Func();
Console.WriteLine(p.name); // prints null
}
}
Related
As far as I understand, the default type or argument passing in c# is by value. Therefore no statement is required. But when I try the run following code, my A matrix in Main is being modified by the operations done to dMatrixU in the Factorize() method of class Decomposition. I'm sure the problem is in the constructor of the Decomposition when I just assing A to dMatrixU, the reference of A is being assigned instead of the values. Therefore my question on how to avoid this, all I have found is how to pass the arguments by reference. Again, as I understand no modifier is needed for passing the argument by value. Where am I wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LinearEquations;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
double[,] A = new double[,]
{ { 1, 1, 1 } ,
{ 4, 3, -1 } ,
{ 3, 5, 3 } };
double[] B = new double[] {1,6,4};
Decomposition lu = new Decomposition(A,B);
lu.Factorize();
PrintMatrix(A,"A:");
PrintVector(B,"B:");
PrintMatrix(lu.L,"L:");
PrintMatrix(lu.U,"U:");
PrintVector(lu.D,"D:");
}
public static void PrintMatrix(double[,] M, String Title = "Matrix: ")
{
Console.WriteLine(Title);
for(int i = 0; i<M.GetLength(0); i++)
{
for(int j = 0; j<M.GetLength(1);j++)
{
Console.Write(M[i,j]+"\t");
}
Console.Write("\n");
}
Console.Write("\n");
}
public static void PrintVector(double[] V, String Title = "Vector: ",bool AsRow = true)
{
String str = (AsRow)? "\t" : "\n";
Console.WriteLine(Title);
for(int i = 0; i<V.GetLength(0); i++)
{
Console.Write(V[i]+str);
}
Console.WriteLine("\n");
}
}
}
namespace LinearEquations
{
public class Decomposition
{
// Fields
private double[,] dMatrixA; // Parameter in A*X=B
private double[] dVectorB; // Parameter in A*X=B
private double[] dVectorX; // Result wanted in A*X=B
private double[,] dMatrixU; // A splits into L and U
private double[,] dMatrixL; // L is used to calculate D in L*D=B
private double [] dVectorD; // D is used to calculate X in U*X=D
// Properties
public double[,] A
{
get { return dMatrixA; }
set { dMatrixA = value; }
}
public double[] B
{
get { return dVectorB; }
set { dVectorB = value; }
}
public double[] X
{
get { return dVectorX; }
set { dVectorX = value; }
}
public double[,] L
{
get { return dMatrixL; }
set { dMatrixL = value; }
}
public double[,] U
{
get { return dMatrixU; }
set { dMatrixU = value; }
}
public double[] D
{
get { return dVectorD; }
set { dVectorD = value; }
}
// Constructor
public Decomposition(double[,] A, double[] B)
{
dMatrixA = A;
dVectorB = B;
dVectorX = new double[B.Length];
dMatrixU = A;
dMatrixL = new double[A.GetLength(0),A.GetLength(1)];
dVectorD = new double[B.Length];
}
// Split A into L and U
public void Factorize()
{
// Iterate per each row
for(int i = 0; i<dMatrixU.GetLength(0); i++)
{
// For all the rows make element i equals 0
for(int j = i+1; j<dMatrixU.GetLength(0);j++)
{
// Factor that assures substraction makes 0
dMatrixL[1,1] = dMatrixU[j,i] / dMatrixU[i,i];
// Iterate per each column
for(int k = 0; k<dMatrixU.GetLength(1);k++)
{
dMatrixU[j,k] = dMatrixU[j,k] - dMatrixU[i,k]*dMatrixL[1,1];
}
}
}
}
}
}
As far is i understand, the default type or argument passing in c# is by value.
Unfortunately it is a bit more complicated and also has some execptions:
Reference types like Decomposition you hand in by making a copy of the reference. Unfortunately that means both still reference the same instance in memory. So despite a copy operation, it is call-by-Reference.
With value types like Int or double and their aliases, usually a copy is made. I do not know of any case where it does not, but I was wrong on those things before. So they are call by value.
Finally String and a few other reference types are inmutable by design. That has the advantage that they behave kinda like value types in this area. You hand in a Reference, but the instance itself can not be changed. The code can only create a new instance in memory with a different value. So despite handing over literal references, it kinda works like call by value.
Your specific case
Arrays are very explicitly Reference types. Handing them into a function without side effects, requires proper cloning. If it is a array of reference types, the cloning must be deep.
In your case you have arrays of value types. If you want to avoid call-by-reference side effects, you those arrays must be cloned. However as double is a value type, this cloning can be shallow. No need for a deep clone.
Unlike Java there is not a dedicated Clone() Method. And I am not sure why exactly. However you can often use one Collection to initialize another through the constructor. Or they even have a function like Array.Copy(), as TheBatman pointed out.
I have a collection of object in lst of type DataResponse and what I would like to do is sum up all the properties that are int and decimal of this collection and assign the result of each property to another object DataContainerResponse that has the same exact property names(and types) as the those that are being summed up.
I can do this manually by typing out each property by hand and do a .Sum(s=>s.<propertyname>. But that so 90s. Below is my fruitless attempt to juice it out. Frankly, I never assigned a var to a lambda expression before and I don't even know if it's possible .Sum(s=><var name>);
public DataAggragationResponse doAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataContainerResponse rd = new DataContainerResponse();
//If I do it manually typing each prop by hand.
rd.VIOL = lst.Sum(s => s.VIOL);
//Automation!!!
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
rd.GetType().GetProperties().SetValue(lst.Sum(s => propertyInfo.Name[0]));
}
}
If you want to go with full reflection, you can try something like the following. I didnt optimize the code, did it as fast as I can. So sorry for the messy look and Im assuming the property names are same in the aggregated result class and the unit class that you are aggregating against.
class Program
{
static void Main(string[] args)
{
var list = new List<DataResponse>();
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
Stopwatch watch = new Stopwatch();
watch.Start();
var response = DoAggregationReflection(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
watch.Reset();
watch.Start();
var response2 = DoAggregation(list);
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
}
public static DataAggragationResponse DoAggregationReflection(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
var responseType = typeof(DataResponse);
var aggrResponseType = typeof(DataAggragationResponse);
foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
{
aggrResponseType.GetProperty(propertyInfo.Name).SetValue(aggrResponse, lst.Sum(x => (int)responseType.GetProperty(propertyInfo.Name).GetValue(x)));
}
return aggrResponse;
}
public static DataAggragationResponse DoAggregation(List<DataResponse> lst)
{
if (lst.Count == 0)
return null;
DataAggragationResponse aggrResponse = new DataAggragationResponse();
aggrResponse.Stuff = lst.Sum(x => x.Stuff);
aggrResponse.Stuff2 = lst.Sum(x => x.Stuff2);
return aggrResponse;
}
}
public class DataResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
public class DataAggragationResponse
{
public int Stuff { get; set; }
public int Stuff2 { get; set; }
}
But, as a suggestion, if you want to go with this approach, its better if you can cache all the reflection invokes you're making as they are costly. And the 90's approach would still win in benchmark. Like the example above would benchmark like the following with the simple StopWatch.
1.8193
0.4476
Press any key to continue . . .
The first one is the execution time of DoAggregationReflection and the last one is the execution time of DoAggregation. You can optimize the reflection one as much as you want but I think it would still fail to compete with the basic one.
Sometime's the 90's are way better. ;) Although you'd still use LINQ to do the actual summation so that's not that 90's anymore as LINQ was born in 2007 according to wikipedia.
Hopefully this can help you. I wish I had kept the SO link to the question I pulled this from a while ago. Sorry to the original poster for not mentioning his/her name.
using System.Reflection;
public static Dictionary<string, string> GetPropertiesValue(object o)
{
Dictionary<string, string> PropertiesDictionaryToReturn = new Dictionary<string, string>();
foreach (MemberInfo itemMemberInfo in o.GetType().GetMembers())
{
if (itemMemberInfo.MemberType == MemberTypes.Property)
{
//object PropValue = GetPropertyValue(OPSOP, item.Name);
//string itemProperty = itemMemberInfo.Name;
//string itemPropertyValue = o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString();
//Console.WriteLine(itemProperty + " : " + itemPropertyValue);
PropertiesDictionaryToReturn.Add(itemMemberInfo.Name, o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString());
}
}
return PropertiesDictionaryToReturn;
}
It's not exactly what you need but, I think you could adapt it.
I would rather take a different approach. I would dynamically build and compile (once) something like this:
Func<DataContainerResponse, DataResponse, DataContainerResponse> aggregateFunc =
(result, item) =>
{
result.Prop1 += item.Prop1;
result.Prop2 += item.Prop2;
...
result.PropN += item.PropN;
return result;
}
(if you wonder why the signature is like the above, the answer is - because it can be used directly for the following Aggregate overload).
Here is how it can be done:
static readonly Func<DataContainerResponse, DataResponse, DataContainerResponse>
AggregateFunc = BuildAggregateFunc();
static Func<DataContainerResponse, DataResponse, DataContainerResponse> BuildAggregateFunc()
{
var result = Expression.Parameter(typeof(DataContainerResponse), "result");
var item = Expression.Parameter(typeof(DataResponse), "item");
var propertyTypes = new HashSet<Type> { typeof(decimal), typeof(int) };
var statements = item.Type.GetProperties()
.Where(p => propertyTypes.Contains(p.PropertyType))
.Select(p => Expression.AddAssign(
Expression.Property(result, p.Name),
Expression.Property(item, p)));
var body = Expression.Block(statements
.Concat(new Expression[] { result }));
var lambda = Expression.Lambda<Func<DataContainerResponse, DataResponse, DataContainerResponse>>(
body, result, item);
return lambda.Compile();
}
and the usage is simple:
public DataContainerResponse DoAggregation(List<DataResponse> source)
{
return source.Aggregate(new DataContainerResponse(), AggregateFunc);
}
I have a class which has a bunch of collections
Class MyInfoClass
{
Queue<float> firstQ = new Queue<float>();
Queue<float> secondQ = new Queue<float>();
Queue<float> thirdQ = new Queue<float>();
Queue<float> fourthQ = new Queue<float>();
Queue<float> fifthQ = new Queue<float>();
}
And another which has a string representation of the names of those collections
class myParamClass
{
internal static string[] Chain =
{
"firstQ",
"secondQ",
"thirdQ",
"fourthQ",
"fifthQ"
}
}
I want to access the collections using the string representations
class Program
{
static void Main(string[] args)
{
MyInfoClass myInfoChain = new MyInfoClass();
float i = 0;
//attempt to set
foreach (string qName in MyParamClass.Chain)
{
i++;
myInfoChain.GetType().GetProperty(qName).SetValue(myInfoChain,i);
}
//attempt to get
foreach (string qName in MyParamClass.Chain)
{
Trace.WriteLine(myInfoChain.GetType().GetProperty(qName).GetValue(myInfoChain,null));
}
}
}
I'm trying to use reflection but keep getting null object exceptions, but don't really care how I achieve this. I tried using an enum but hat was over complicating things, I've tried exposing the fields as properties and invoking the get set methods. I'm going round and round like a hamster.
Specifically I need to know how to access the collections as above, but in general how to I access the members (properties/field/method) of a class using a string of the (property/field/method) name would be very useful?
What I have read keeps pointing to something like
var letMeIn = typeof (MyInfoClass).GetProperty("_firstQ").GetValue(myInfoChain,null); ????
but GetProperty("_firstQ") is null no matter what I do.
Any help is appreciated.
Resolved as follows
With respect to reflection exposing the queues as properties and using
this was a good solution,
var letMeIn = typeof (MyInfoClass).GetProperty("FirstQ")
.GetValue(myInfoChain,null);
... but I never managed to get the matching .SetValue() method working.
What worked really well was the array of Queues
class MyInfoClass
{
private Queue<float> _firstQ = new Queue<float>();
private Queue<float> _secondQ = new Queue<float>();
private Queue<float> _thirdQ = new Queue<float>();
private Queue<float> _fourthQ = new Queue<float>();
private Queue<float> _fifthQ = new Queue<float>();
public Queue<float>[] MyQueues
{
get
{
return new[] { _firstQ, _secondQ, _thirdQ, _fourthQ, _fifthQ };
}
}
}
Which allowed me to iterate the collections byte index, and also to
leverage LINQ in recusive loops (my core objective) as shown below
.
class Program
{
static List<double> SeperateWorkMethod(List<double> d, float val)
{
//stuff/work on d, List<double>
return d;
}
static void Main(string[] args)
{
MyInfoClass myInfoChain = new MyInfoClass();
float x = 0;
List<double> temp = new List<double>();
foreach (Queue<float> t in myInfoChain.MyQueues)
{
x = x + 123;
t.Enqueue(x);
temp.Add(t.Average());
temp.AddRange(t.Select(subType => subType.customField));
}
for (int index = 0; index < myInfoChain.MyQueues.Length; index++)
{
myInfoChain.MyQueues[index].Aggregate(temp, SeperateWorkMethod);
}
}
}
}
I still feel like I should use an enum here somewhere, but the puzzle
is solved for now. Many thanks to all.
You don't have properties, you have fields. So you need GetField method instead.Also the fields are private, you need to specify BindingFlags.NonPublic flag:
myInfoChain.GetType()
.GetField(qName, BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(myInfoChain,i);
but GetProperty("_firstQ") is null no matter what I do.
That's because you don't have a property _firstQ. Add one... (in addition to your fields)
public Queue<float> _firstQ { get { return firstQ; } }
and your code example above should work.
Note, though, that in C# properties usually start with an upper-case letter, so
public Queue<float> FirstQ { get { return firstQ; } }
would be more idiomatic.
In fact, if all you need to do is access the n-th queue dynamically, you don't need reflection at all:
public Queue<float>[] MyQueues
{
get
{
return new[] { firstQ, secondQ, thirdQ, fourthQ, fifthQ };
}
}
and you can just access the n-th queue as myInfoChain.MyQueues[n]... no reflection needed.
At the moment your queues are fields, not properties. Try defining them as
FirstQ { get; set; }
and assigning the queue in the constructor. Then GetProperty("FirstQ") should get you what you need.
I'd like to parse a text file with a few dozen entries. Right now, I have a dumbed-down solution that reads line by line and compares against hard-coded strings:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
cmpStr = "TIME_YEAR";
try
{
if (line.Equals(cmpStr))
GlobalData.time_year = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
catch { }
// ... repeat to parse the remaining lines
}
GlobalData is a static class and looks like this:
public static class GlobalData
{
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
// other entries omitted
public static string[] GlobalKeywords = new string[37]
{
"MODE",
"TIME_YEAR",
"TIME_MONTH",
"TIME_DAY",
"TIME_HOUR",
"TIME_MINUTE",
// other entries omitted
};
}
If it were possible to access my static fields by index, I'd do:
int i = 0;
while ((line = reader.ReadLine()) != null)
{
cmpStr = GlobalData.GlobalKeywords[i]; // when i == 0: cmpStr = "MODE"
if (line.Equals(cmpStr))
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
// GlobalData[0] would be GlobalData.mode, and so on (but doesn't work)
i++;
}
catch { }
So, even though I can setup a loop to compare against a string array of keywords,
how do I assign a certain field of my static class ?
br
Chris
I'm not sure what your business constraints are, so it's hard to propose a fool-proof solution, though a few points:
cmpStr = "MODE";
try
{
if (line.Equals(cmpStr))
GlobalData.mode = Convert.ToInt32(line.Remove(0, cmpStr.Length));
}
This won't work as you (probably expect) - if line.Equals("MODE") then line.Remove(0, "MODE".Length) is an empty string. What you probably want is line.StartsWith(cmpStr) or line.Contains(cmpStr).
GlobalData is a static class
This doesn't seem a good approach for what you're doing. You may want to read up on static classes and when to use them (MSDN is a good starting point, though it obviously can't cover everything: http://msdn.microsoft.com/en-us/library/79b3xss3%28v=vs.80%29.aspx).
Other than that, you can probably simply replace all your int fields with a dictionary (though please rethink the static approach as described above):
public static Dictionary<String, int> Items = new Dictionary<String, int>();
Then your parsing code could look like this:
while ((line = reader.ReadLine()) != null) //returns null if end of stream
{
var matchingString
= GlobalData.GlobalKeywords.FirstOrDefault(s => line.StartsWith(s));
if (matchingString != null)
GlobalData[matchingString]
= Convert.ToInt32(line.Remove(0, matchingString.Length));
}
You will then be able to fetch that data using e.g. GlobalData.Items["MODE"].
One last bit: you may consider introducing constant values in your global data class, e.g.:
public const String MODE = "MODE";
Then you can use GlobalData.Items[GlobalData.MODE] and avoid typos: writing GlobalData.Items[GlobalData.MODe] would cause a compile error.
Replace this:
public static int mode;
public static int time_year;
public static int time_month;
public static int time_day;
public static int time_hour;
public static int time_minute;
With this:
public static Dictionary<string, int> my_values = new Dictionary<string, int>();
Then replace:
GlobalData[i] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
with:
GlobalData.my_values[cmpStr] = Convert.ToInt32(line.Remove(0, cmpStr.Length));
That should do what you want even though I don't understand how you expect the Convert.ToInt32 to work. The way you are calling Remove will create an empty string (which might convert to 0, I can't remember) and even if it didn't, the line doesn't contain a number because you compared it successfully to a string like "MODE".
An elegant way to solve your problem is to prepare a different action for each of the acceptable strings. You use a Dictionary(Of String, <Action>) where Action is a common delegate type that receive a string in input and know how to process it accordingly to the keyword present at the beginning of the line.
// The common signature for every methods stored in the value part of the dictionary
public delegate void ParseLine(string line);
// Global dictionary where you store the strings as keyword
// and the ParseLine as the delegate to execute
Dictionary<String, ParseLine> m_Actions = new Dictionary<String, ParseLine>() ;
void Main()
{
// Initialize the dictionary with the delegate corresponding to the strings keys
m_Actions.Add("MODE", new ParseLine(Task1));
m_Actions.Add("TIME_YEAR", new ParseLine(Task2));
m_Actions.Add("TIME_MONTH", new ParseLine(Task3));
m_Actions.Add("TIME_DAY", new ParseLine(Task4));
.....
while ((line = reader.ReadLine()) != null)
{
// Search the space that divide the keyword from the value on the same line
string command = line.Substring(0, line.IndexOf(' ')).Trim();
// a bit of error checking here is required
if(m_Actions.ContainsKey(command))
m_Actions[command](line);
}
}
void Task1(string line)
{
// this will handle the MODE line
GlobalData.Mode = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task2(string line)
{
GlobalData.time_year = Convert.ToInt32(line.Substring(line.IndexOf(' ')+1).Trim());
}
void Task3(string line)
{
.....
}
void Task4(string line)
{
.....
}
A simple (and not really clean) approach is to add an indexer to your global data class and decide which field to set based on the index. But you have to extend the indexer every time you add a field (basically you move the if/switch from the while Loop into the indexer).
You could also use reflection, if you can match the keyword to the field name. This is not very performant but does not need to be extended as long as you can map the keyword to the new field name.
Another approach is to create a dictionary>. In this dictionary you register the keywords, e.g. (pseudo-code):
Class Level variable:
private keywordsDict = new Dictionary<string, Action<int>>();
In a constructor:
keywordsDict.Add("MODE", delegate(value) GlobalData.mode = value);
In while-loop:
var action = keywordsDict[line];
action(value);
In the later approach, you only need to extend the dictionary but not the algorithm as such if you have a new keyword/field.
May be i can tell you how to achieve it (GlobalData[i]) in C# thought its not the answer you are looking for.
class GlobalData
{
private string[] array = new string[10];
public GlobalData()
{
//now initialize array
array[0] = "SomeThingA";
array[1] = "SomeThingB";//continue initialization.
}
public string this[int index]
{
get {return array[index];}
}
}
Now the clients can use GlobalData like ,
GlobalData gd = new GlobalData();
gd[1] = "SomeOtherThing" ; //set the value.
string value = gd[1];//get the value
But this cant be done by making the class static as you see it works with 'this'
I'm trying to make a deep copy of a generic list, and am wondering if there is any other way then creating the copying method and actually copying over each member one at a time. I have a class that looks somewhat like this:
public class Data
{
private string comment;
public string Comment
{
get { return comment; }
set { comment = value; }
}
private List<double> traceData;
public List<double> TraceData
{
get { return traceData; }
set { traceData = value; }
}
}
And I have a list of the above data, i.e List<Data>. What I'm trying to do is plot a trace data of the subset of List onto a graph, possibly with some scaling or sweeping on the data. I obviously don't need to plot everything in the list because they don't fit into the screen.
I initially tried getting the subset of the list using the List.GetRange() method, but it seems that the underneath List<double> is being shallow copied instead of deep copied. When I get the subset again using List.GetRange(), I get previously modified data, not the raw data retrieved elsewhere.
Can anyone give me a direction on how to approach this? Thanks a lot.
The idiomatic way to approach this in C# is to implement ICloneable on your Data, and write a Clone method that does the deep copy (and then presumably a Enumerable.CloneRange method that can clone part of your list at once.) There isn't any built-in trick or framework method to make it easier than that.
Unless memory and performance are a real concern, I suggest that you try hard to redesign it to operate on immutable Data objects, though, instead. It'll wind up much simpler.
You can try this
public static object DeepCopy(object obj)
{
if (obj == null)
return null;
Type type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
else if (type.IsArray)
{
Type elementType = Type.GetType(
type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = 0; i < array.Length; i++)
{
copied.SetValue(DeepCopy(array.GetValue(i)), i);
}
return Convert.ChangeType(copied, obj.GetType());
}
else if (type.IsClass)
{
object toret = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = type.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
object fieldValue = field.GetValue(obj);
if (fieldValue == null)
continue;
field.SetValue(toret, DeepCopy(fieldValue));
}
return toret;
}
else
throw new ArgumentException("Unknown type");
}
Thanks to DetoX83 article on code project.
If IClonable way is too tricky for you. I suggest converting to something and back. It can be done with BinaryFormatter or a Json Converter like Servicestack.Text since it is the fastest one in .Net.
Code should be something like this:
MyClass mc = new MyClass();
string json = mc.ToJson();
MyClass mcCloned = json.FromJson<MyClass>();
mcCloned will not reference mc.
The most easiest (but dirty) way is to implement ICloneable by your class and use next extension method:
public static IEnumerable<T> Clone<T>(this IEnumerable<T> collection) where T : ICloneable
{
return collection.Select(item => (T)item.Clone());
}
Usage:
var list = new List<Data> { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } };
var newList = list.Clone();
another thing you can do is mark your class as serializable and use binary serialization.
Here is a working example
public class Program
{
[Serializable]
public class Test
{
public int Id { get; set; }
public Test()
{
}
}
public static void Main()
{
//create a list of 10 Test objects with Id's 0-10
List<Test> firstList = Enumerable.Range(0,10).Select( x => new Test { Id = x } ).ToList();
using (var stream = new System.IO.MemoryStream())
{
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(stream, firstList); //serialize to stream
stream.Position = 0;
//deserialize from stream.
List<Test> secondList = binaryFormatter.Deserialize(stream) as List<Test>;
}
Console.ReadKey();
}
}
If you make your objects immutable you don't need to worry about passing around copies of them, then you could do something like:
var toPlot = list.Where(d => d.ShouldBePlotted());
Since your collection is mutable, you need to implement the deep copy programmatically:
public class Data
{
public string Comment { get; set; }
public List<double> TraceData { get; set; }
public Data DeepCopy()
{
return new Data
{
Comment = this.Comment,
TraceData = this.TraceData != null
? new List<double>(this.TraceData)
: null;
}
}
}
The Comment field can be shallow copied because its already an immutable class. You need to create a new list for TraceData, but the elements themselves are immutable and require no special handling to copy them.
When I get the subset again using
List.GetRange(), I get previously
modified data, not the raw data
retrieved elsewhere.
Use your new DeepCopy method as such:
var pointsInRange = dataPoints
.Select(x => x.DeepCopy())
.GetRange(start, length);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeepListCopy_testingSome
{
class Program
{
static void Main(string[] args)
{
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
//populate list1
for (int i = 0; i < 20; i++)
{
list1.Add(1);
}
///////
Console.WriteLine("\n int in each list1 element is:\n");
///////
foreach (int i in list1)
{
Console.WriteLine(" list1 elements: {0}", i);
list2.Add(1);
}
///////
Console.WriteLine("\n int in each list2 element is:\n");
///////
foreach (int i in list2)
{
Console.WriteLine(" list2 elements: {0}", i);
}
///////enter code here
for (int i = 0; i < list2.Count; i++)
{
list2[i] = 2;
}
///////
Console.WriteLine("\n Printing list1 and list2 respectively to show\n"
+ " there is two independent lists,i e, two differens"
+ "\n memory locations after modifying list2\n\n");
foreach (int i in list1)
{
Console.WriteLine(" Printing list1 elements: {0}", i);
}
///////
Console.WriteLine("\n\n");
///////
foreach (int i in list2)
{
Console.WriteLine(" Printing list2 elements: {0}", i);
}
Console.ReadKey();
}//end of Static void Main
}//end of class
}
One quick and generic way to deeply serialize an object is to use JSON.net. The following extension method allows serializing of a list of any arbitrary objects, but is able to skip Entity Framework navigation properties, since these may lead to circular dependencies and unwanted data fetches.
Method
public static List<T> DeepClone<T>(this IList<T> list, bool ignoreVirtualProps = false)
{
JsonSerializerSettings settings = new JsonSerializerSettings();
if (ignoreVirtualProps)
{
settings.ContractResolver = new IgnoreNavigationPropsResolver();
settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.Formatting = Formatting.Indented;
}
var serialized = JsonConvert.SerializeObject(list, settings);
return JsonConvert.DeserializeObject<List<T>>(serialized);
}
Usage
var clonedList = list.DeepClone();
By default, JSON.NET serializes only public properties. If private properties must be also cloned, this solution can be used.
This method allows for quick (de)serialization of complex hierarchies of objects.