I'm looking for a type/method of collection where I can add an object to a group of objects, then separately change the attributes of that object, and have those changes reflected in the object within the collection.
I've heard that List<T> adds values by reference, so I figured that the reference would be to the same object. In other words, I assumed:
List<string> valuesList = new List<string>();
string initialValue = "Alpha";
valuesList.Add(initialValue);
initialValue = "Bravo";
bool incorrectAssumption = (valuesList[0] == "Bravo");
I had hoped that 'valuesList' would then contain the new value, "Bravo." Tried it out and I realized that the List copies the reference, it doesn't absorb it, so valueList still only has the "Alpha" value. Are there any ways to use a collection as a legitimate handful of the objects they contain?
And in case it helps to see the actual business need....
List<BaseWidget> widgets = new List<BaseWidget>();
DerivedWidget specialWidget = new DerivedWidget();
DerivedWidget extraSpecialWidget = new DerivedWidget();
widgets.Add(specialWidget);
widgets.Add(extraSpecialWidget);
specialWidget.Run();
extraSpecialWidget.Run();
if (!widgets.Any(x => x.RunSuccessfully)) return false;
(Where the Run() method sets the RunSuccessfully property, which I'd like to have reflected in the 'widgets' list.)
============================================================================
UPDATE
As it's been pointed out in the answers and comments, there's a bit of a discrepancy between the business need mock-up and the dry-run example. I'll condense the life-lesson into this: it seems List<objects> have their changes tracked, whereas List<values> don't.
Well. It seems that you don't understand what happens really. Here is great article about .net type internals.
Shortly, what happens in your example with strings:
You create list
You create variable initialValue of string type. Value of this variable stores in special local variables container. Because string is reference type, in container of local variables it contained as a pointer to object.
You create new string "Alpha", storing it in heap, and assign pointer (to this string) to your local variable.
Then you are adding object to list. In your List this object stored as pointer to somewhere.
Then you are changing content of local variable 'initialValue' by assign it to pointer to another string. So, now in local variable 'initialValue' is one pointer, in list is another pointer.
Well, what about solutions?
Wrap your string to some another class. Like this:
class Wrapper<T> {
public T Content {get;set;}
public Wrapper(T content) {
Content = content;
}
}
Usage:
void Main()
{
var valuesList = new List<Wrapper<string>>();
var initialValue = new Wrapper<string>("Alpha");
valuesList.Add(initialValue);
initialValue.Content = "Bravo";
Console.WriteLine(valuesList[0].Content);
}
A bit ugly syntax.
Use clojures:
void Main()
{
List<Func<string>> valuesList = new List<Func<string>>();
string initialValue = "Alpha";
valuesList.Add(() => initialValue);
initialValue = "Bravo";
Console.WriteLine(valuesList[0]() == "Bravo");
}
All references to non-value types will be passed by reference, List<T> or not. String is a value type, however, and will always be passed by value. They are also immutable, so any time you change one you're actually creating a new String.
For your example, you could create a wrapper type to contain your string, and store this in your List<T>.
It seems that your actual business case should work properly, unless they are declared as structs.
Related
I'm new with C# OOP, and it's a little bit more difficult than JS to put Objects into Array. This is my first exercise, any help on how to put an object into an array is appreciated.
I was hoping the output be cars = { porsche, mercedes, bmw, punto, ferrari};
What I'm doing wrong?
static void Main(string[] args)
{
// Create 5 Car Objects and add them to Array
var porsche = new Car("Porshe", 340);
var mercedes = new Car("Mercedes", 320);
var bmw = new Car("BMW", 330);
var punto = new Car("Punto", 220);
var ferrari = new Car("Ferrari", 380);
// Cars to array
object[] cars = new object[5];
cars[0] = porsche;
cars[1] = mercedes;
cars[2] = bmw;
cars[3] = punto;
cars[4] = ferrari;
foreach(var car in cars)
{
Console.WriteLine($"some {car}");
}
}
Car
class Car
{
public Car(string model, int speed)
{
Model = model;
Speed = speed;
}
public string Model { get; set; }
public int Speed { get; set; }
public int CalculateSpeed( int speed, int skill)
{
return speed * skill;
}
}
Console Output:
some OOP_Car_Human.Car
some OOP_Car_Human.Car
some OOP_Car_Human.Car
some OOP_Car_Human.Car
some OOP_Car_Human.Car
As I mentioned in the comments, C# is strongly typed. Instead of creating an array of objects (into which any object of any type can be added), instead, create an array of cars:
Car[] cars = new Car[5];
Now, your array can contain nothing but Car objects (or instances of Car sub-classes). No one can say cars[2] = new Horse("Secretariat"); (something that can be done with a object[] array.
You can also use var instead:
var cars = new Car[5];
That way, you don't have to type the type specification twice. The two statements have completely the same meaning. You can only use var when the type of the right-hand side of the assignment can be deduced by the compiler from context. That type is applied to the variable on the left-hand side.
You can also initialize an array when you create it:
var cars = new Car[] {
porsche,
mercedes,
bmw,
punto,
ferrari,
};
When you do that, you don't have to include the array size in the array constructor expression (you can, but you don't have to).
You can also do this:
var cars = new Car[] {
new Car("Porshe", 340),
new Car("Mercedes", 320),
new Car("BMW", 330),
new Car("Punto", 220),
new Car("Ferrari", 380),
};
You don't use the names porshe, mercedes, etc. other than for the array initialization, so you don't really need them. You can use them (and it likely won't cost any more at runtime, but...)
As #crowcoder has pointed out, you really should add a ToString method:
public override string ToString()
{
return Model;
}
That will make your code behave the way you expect. The Console.WriteLine method, when it's passed a string will output a string. However, if it is passed an object of any other type, it will call ToString on the object. The object class declares ToString as a virtual or overridable method. The object class includes an implementation of ToString, but the only thing it knows about an object is the type of the object, so that is what it uses as the returned string. Since the method is virtual/overridable, sub-classes of object (i.e., just about every type) can override that method and do whatever they'd like. Your expectation was that the car's Model property would be output, so that's what this does.
Other folks are pushing you away from arrays and towards List. There are good reasons to do this. One of them (familiar to Javascript programmers) is that List is stretchy; it will grow as you add things (like a Javascript array does). C# arrays are necessarily fixed size.
One other note. As you found out when you had an array of objects that contained Cars, C#'s type-safety will prevent you from directly calling Car-specific methods on objects that the compiler knows only as objects. In C#, a variable types as object can refer to anything, but it only has a very small number of methods (like ToString) that can be called on it directly (unlike Javascript, where member binding is done at runtime rather than compile time).
If you want to get Javascript-like variable behavior, you can use dynamic as the type (var cars = new dynamic[]{bmw, etc.}). Variables typed dynamic can behave very much like Javascript variables (and there are types that behave nearly exactly like Javascript objects (they derive from Expando)).
If you have an expression like (like you showed): Console.WriteLine($"some {car.Model}");, and car is typed dynamic, then, at runtime, the type of car is examined to determine if it has a Model property. If it does, it is evaluated. If it doesn't, it throws an exception. All this is done at runtime. They error that you saw (CS1061:'object' does not contain a definition for 'Model') happens at compile time. Because of type-safety, a Car is known to have a Model property and no-runtime check needs to be done.
That said, nearly everyone monitoring the C# tag will tell you dont' use dynamic unless you have a really good reason to. C#'s type-safety is its big advantage (and once you get used to it, it just makes sense).
I'd go for using a List.
List<Car> cars = new List<Car>();
cars.Add(porsche);
cars.Add(mercedes);
...
cars.Add(ferrari);
cars.ForEach(car => {
Console.WriteLine($"some {car.Model}");
});
If you want to turn it into an array, use:
cars.ToArray();
Currently I am receiving an array of objects from a database.
object [] sqlResultData = DatabaseCall.Result();
This array of objects needs to be matched to class variables like this
CClassOfVars classVar = new CClassOfVars();
classVar.myProperty = sqlResultData[0];
classVar.myProperty1 = sqlResultData[1];
What i wish to do is pass the list of propertys on the class in order to a function and have the mapping from the object array occur automatically based on the order.
For example:
Method defined like this
FillData(object [] databaseValues, IList<object>())
Called like this
CClassOfVars classVar = new CClassOfVars();
object [] sqlResultData = DatabaseCall.Result();
FillData(sqlResultData, new List<object>(){classVar.myProperty,classVar.myProperty1});
The FillData function would hopefully type cast and set the values of myProperty and myProperty1 to the values in array locations of 0,1 etc...
Something like this
FillData(object [] databaseValues, IList<object> mapMe)
{
for (int i = 0; i < mapMe.Count; i++)
{
mapMe[i] = CastToTheCorrectType(mapMe[i], databaseValues[i]);
}
}
Cast to the correct type could look like this?? I took from here: cast object with a Type variable
public T CastToTheCorrectType<T>(T hackToInferNeededType, object givenObject) where T : class
{
return givenObject as T;
}
How can i pass a list of different object types to all have there values modified and assigned within a different function?
The matter you asking about is dark and difficult to be implemented through just a function. There are frameworks out there dealing with object relational mapping. If it is an option, install and learn some OR/M. If not ... well, there might be some dirty way.
You can use the JSON.NET library to do the heavy lifting for you. It's super easy to use and install through Nuget. My point is as follows.
Construct an anonymous object. Use the property names of the original object.
Fill it with the data from the object array. Spin a loop over the object array...
Serialize the anonymous object.
Deserialize the JSON string into the target type.
At this point, JSON.NET will handle property mapping for you.
List item
E.g. if your target type is Person you might do this:
var x = new
{
FirstName = String.Empty,
LastName = String.Empty
};
var persons = new List<Person>(sqlResultData.Length);
foreach (var record in sqlResultData)
{
x.FirstName = record[0];
x.LastName = record[1];
var s = JsonConvert.SerializeObject(x)`
var personX = JsonConvert.Deserialize<Person>(s);
persons.Add(person);
}
In a class constructor I have a instantiate a list containing properties:
public MDInstrument() : base()
{
// bidss = new TickData[] {Bid0};
Bids = new List<TickData> { Bid0, Bid1, Bid2, Bid3, Bid4, Bid5, Bid6, Bid7, Bid8, Bid9, Bid10, Bid0, Bid11, Bid13, Bid14, Bid15, Bid6, Bid17, Bid18, Bid19};
Offers = new List<TickData> { Ask0, Ask1, Ask2, Ask3, Ask4, Ask5, Ask6, Ask7, Ask8, Ask9, Ask10, Ask0, Ask11, Ask13, Ask14, Ask15, Ask6, Ask17, Ask18, Ask19};
}
A method in the class updates the object in the list but why is the object always null ?
I must be missing something
Your problem is that to begin with Bid{x} and Ask{x} have not been instantiated, i.e. they're null, and then you store a reference to those values, and of course the reference is null. When you then later on update Bid0 (for example), then that reference is updated, but nothing can know that this is intended to be stored within your set.
Suggest that you change your list to be an array of a fixed known size (here, 20) which will be all nulls to begin with. Then change your getter/setter accessors for the individual Bid items to actually the array internally. Then you also don't need all of those separate Bid{x}/Ask{x} variables.
This question already has answers here:
Modify Struct variable in a Dictionary
(5 answers)
Closed 7 years ago.
I have the following code that will modify a property inside a structure, and the structure is inside a hash table. Each item in hash table has key of (Int) data type and key of (struct Bag), here is the code i have:
struct Bag {
string apple_type;
string orange_type;
};
// Make a new hashtable that will have a key of (int) data type, value of (Bag)
public static Hashtable bags = new Hashtable();
Then i have a method that will read data from a data base reading rows and adding as long there is a row it will add an item (bag(object)) to the hashtable:
public void initHashtbl(){
OleDbConnection db_connector = new OleDbConnection(connection_string);
// Open connection to oracle
db_connector.Open();
OleDbCommand sql_commander = new OleDbCommand(sql_statement, db_connector);
OleDbDataReader data_Reader = sql_commander.ExecuteReader();
// Read data from sql db server and store it in memory ...
Bag tempBag = new Bag();
// row counter used for list view
int row_counter = 0;
while (data_Reader.Read()) { // Keep reading data from memory until there is no row
tempBag.apple_type = data_Reader[0].ToString();
bags.Add(row_counter, tempBag);
row_counter++;
}
for(int bag_item=0;bag_item < bags.Count;bag_item++){
// Get orange type value from another method that uses another sql statement from another table in db ..
((bag) bags[bag_item]).orange_type = getOrangeType(((bag) bags[bag_item]).apple_type);
}
}
How can i access the property of structure that is already inside hash table at later time if i wanted to access it?
Edit:
I'm getting this error:
"Cannot modify the result of an unboxing conversion."
Dictionary will not allow me to modify that directly
Neither will a Hashtable. This has nothing to do with Hashtable vs Dictionary. The problem is that your "value" in either case is a value type, so you can't modify it directly within the collection. If you really need a struct, then you'll have to create a new value and put it back into the hashtable:
bags.Add(1, new Bag() {apple_type="apple1",orange_type="orange1"});
//((Bag)bags[1]).apple_type="apple2";
var bag = (Bag)bags[1];
bag.apple_type = "appple2";
bags[1] = bag;
But mutable structs are generally bad, so I would either get the value of the struct right the first time (rather than modifying it ourside of the initial load loop) :
// row counter used for list view
int row_counter = 0;
while (data_Reader.Read()) { // Keep reading data from memory until there is no row
{
var appleType = data_Reader[0].ToString();
Bag tempBag = new Bag() {
apple_type = appleType,
orange_type = getOrangeType(appleType)
};
bags.Add(row_counter, tempBag);
row_counter++;
}
or use a class.
Note that the same code works exactly the same way whether you use a Hashtable or a Dictionary.
Also, since your "key" is just an incrementing number (and not tied to the value at all), you could just as well use a List<Bag> and access the items by index.
This code worked for me to read an item:
string orangeType = ((Bag) bags[0]).orange_type;
To modify an item, I think you must create a new Bag and replace the existing one like this:
bags[0] = newBag;
If you try to modify the properties of a bag in the HashTable, then you get the error you reported above.
Because of how value types work, the boxed Bag is a copy of the original, and "unboxing" it by casting back to Bag creates yet another copy. From the C# language spec:
When a value of a value type is converted to type object, an object
instance, also called a “box,” is allocated to hold the value, and the
value is copied into that box. Conversely, when an object reference is
cast to a value type, a check is made that the referenced object is a
box of the correct value type, and, if the check succeeds, the value
in the box is copied out.
Modifying the copy wouldn't change the original anyway, so it wouldn't make much sense to allow it.
Corrections:
To fix your error and to allow modifications to your HashTable of Bag's you should change your value type to reference type:
public class Bag
{
public string apple_type { get; set; }
public string orange_type { get; set; }
};
So I have 2 classes, both have identical Property names. One class contains different variables: int, strings, bool and DateTime The second class contains only 1 int and the rest are all strings.
Now I want to loop through all the properties, get the value from class1, encrypt that data and save it as a string in obj2, then return it to the main form (to save it in a database later).
public PersoonEncrypted EncryptPersonClass(Class1 object1)
{
PersoonEncrypted persEncrypt = new PersoonEncrypted(); //second class obj
Type type = object1.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = persEncrypt.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (var bothProperties in properties.Zip(properties2, (obj1, obj2) => new { Obj1 = obj1, Obj2 = obj2 }))
{
string value = "";
value = bothProperties.Obj1.GetValue(object1) as string;
if (!string.IsNullOrWhiteSpace(value))
{
string encryptValue = Encrypt(value);
if ((bothProperties.Obj2 != null) && (bothProperties.Obj2.PropertyType == typeof(string)))
{ //!= null check has no effect at all
bothProperties.Obj2.SetValue(persEncrypt, encryptValue, null); //errorLine
}
}
}
return persEncrypt;
}
That is what I came up with until now.
I have, of course, searched for other solutions like this one. This, after applying some own changes, didn't return any errors, but it didn't save any encrypted strings into the class persEncrypt. What I concluded was, from that test, is that it was testing if the value in the second class(persEncrypt in my example) from the particular property was null, while it shouldn't do that, it should make a new instance of that variable and save it in the object class, but removing that check gave me the same error.
you're just .Zip-ing the two lists of PropertyInfo objects, which simply iterates through both lists and doesn't check or sort for any sort of matching. This could result in erroneous behavior depending on the order in which properties appear - consider using a .Join instead to match property names.
This code doesn't check for an indexer on the property before attempting to assign to it without one - any indexed property which is of type string will make it to this point and then throw an exception when you try to set it.
Because this code is calling into Properties, there's the possibility an exception is being thrown by the code of the Property itself. This is where a StackTrace from your exception could reveal much more about what's happening.
Your code also checks for a property of type string directly - when using reflection you should use IsAssignableFrom instead in order to allow for inherited types, though that is unlikely the issue in this one case.