How to edit value data in a dictionary C# [duplicate] - c#

This question already has answers here:
How to update the value stored in Dictionary in C#?
(10 answers)
Closed 9 years ago.
I have a dictionary of members where the key is a unique long ID and the value is an object which contains data on that members name surname and other forms of member details. Is there any way in C# that this can be done?
e.g
dictionary key holds memberID 0 member
id 0 name is bob lives in Italy
bob moves to England
is there a way to update the dictionary in C# so that his entry now says he lives in England?

Assuming that Member (or whatever) is a class, it's simple:
members[0].Country = "England";
You're just updating the object which the dictionary has a reference to. Just to step through it, it's equivalent to:
Member member = members[0];
member.Country = "England";
There's only one object representing Bob, and it doesn't matter how you retrieve it.
In fact, if you already have access to the instance of Member via a different variable, you don't need to use the dictionary at all:
// Assume this will fetch a reference to the same object as is referred
// to by members[0]...
Member bob = GetBob();
bob.Country = "England";
Console.WriteLine(members[0].Country); // Prints England
If Member is actually a struct... well, then I'd suggest rethinking your design, and making it a class instead :)

For classes (at least, those that are mutable) this should be as simple as:
long theId = ...
yourDictionary[theId].Country = "England"; // fetch and mutate
For structs (which should be immutable; or also for immutable classes), you will need to fetch, re-create, and overwrite:
long theId = ...
var oldItem = yourDictionary[theId]; // fetch
var newItem = new SomeType(oldItem.Id, oldItem.Name, "England"); // re-create
yourDictionary[theId] = newItem; // overwrite
(obviously the re-create line needs tweaking to your particular objects)
In the evil evil world of mutable structs (see comments), you can mutate once it is in a variable:
long theId = ...
var item = yourDictionary[theId]; // fetch
item.Country = "England"; // mutate
yourDictionary[theId] = item; // overwrite

dictionary[memberID].Location = "Italy";

Well, I can't outcode Marc or Jon but here's my entry: (I used City instead of Country but the concept is the same.)
using System;
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
var dict = new Dictionary<int, Member>();
dict.Add(123, new Member("Jonh"));
dict.Add(908, new Member("Andy"));
dict.Add(456, new Member("Sarah"));
dict[456].City = "London";
Console.WriteLine(dict[456].MemberName + " " + dict[456].City);
Console.ReadKey();
}
}
public class Member
{
public Member(string name) {MemberName = name; City="Austin";}
public string MemberName { get; set; }
public string City { get; set; }
// etc...
}

Related

What is the "with" operator for in C#?

I've came across this code:
var rectangle = new Rectangle(420, 69);
var newOne = rectangle with { Width = 420 }
I was wondering about with keyword in C# code. What is it for? And how can it be used? And what benefits does it bring to the language?
It's an operator used in expressions for easier duplication of an object, overriding some of it's public properties/fields (optional)
with expression - MSDN
Currently it can only be used with records. But maybe there will be no such restriction in the future (assumption).
Here's an example how it can be used:
// Declaring a record with a public property and a private field
record WithOperatorTest
{
private int _myPrivateField;
public int MyProperty { get; set; }
public void SetMyPrivateField(int a = 5)
{
_myPrivateField = a;
}
}
Now let's see how with operator can be used:
var firstInstance = new WithOperatorTest
{
MyProperty = 10
};
firstInstance.SetMyPrivateField(11);
var copiedInstance = firstInstance with { };
// now "copiedInstance" also has "MyProperty" set to 10 and "_myPrivateField" set to 11.
var thirdCopiedInstance = copiedInstance with { MyProperty = 100 };
// now "thirdCopiedInstance " also has "MyProperty" set to 100 and "_myPrivateField" set to 11.
thirdCopiedInstance.SetMyPrivateField(-1);
// now "thirdCopiedInstance " also has "MyProperty" set to 100 and "_myPrivateField" set to -1.
NOTE for reference types from MSDN:
In the case of a reference-type member, only the reference to a member instance is copied when an operand is copied. Both the copy and original operand have access to the same reference-type instance.
That logic can be modified by modifying the copy constructor of a record type. Quote from MSDN:
By default, the copy constructor is implicit, that is, compiler-generated. If you need to customize the record copy semantics, explicitly declare a copy constructor with the desired behavior.
protected WithOperatorTest(WithOperatorTest original)
{
// Logic to copy reference types with new reference
}
And in terms of what benefits it gives, I think it should be quite obvious now, that it makes copying of instances much easier and convenient.
Basically, the with operator will create a new object instance (records only, for now), by "coping values" from the "source" object and override some named properties in the destination object.
For example, instead of doing this:
var person = new Person("John", "Doe")
{
MiddleName = "Patrick"
};
var modifiedPerson = new Person(person.FirstName, person.LastName)
{
MiddleName = "William"
};
you can do this:
var modifiedPerson = person with
{
MiddleName = "Patrick"
};
Basically, you will write less code.
Use this source to get more details on the example above and official documentation for more examples.
Short answer is the following:
with keyword in C# was added for easier copy of complicated objects, with a possibility to override some of the public properties.
Examples are already briefly provided in the accepted answer.

Can I use string variable to name an object (C#)?

I am a beginner and currently working on a project for practice. My goal is to create an Adress Book application. What I want to do is I am asking the user to pass in a name. And I store that name in a variable. Is it possible for me to use that string variable to name the object? I have looked for solutions and they all suggest to have a Constructor that takes a name and assigns it but I already have that and it is not what I want. I am storing all these Person variables in a Person List(That's why I am using the loop) and later, I want to build a system to browse through Adress Book and search for stuff. So my overall question is- Can I use a string variable to name the object. Is there any way to do that?
while (true)
{
Console.WriteLine("Please enter names to the adress book or type \"quit\" to finish");
var input = Console.ReadLine();
var name = input;
if (string.IsNullOrEmpty(input))
{
throw new IsNullException("Name can not be null or empty");
}
if (input.ToLower() == "quit")
{
break;
}
Person person = new Person(input);
AdressBook.Add(person);
}
No. A variable name is a convenience to the programmer, but it conveys no information to the program. Also note that a variable is just a reference to an object; it is not "the name of the object" (there might actually be many variables that reference the same object).
However, there are situations in which it is convenient to be able to tie an object to another piece of information in order to be able to look the object up by that information later. The general computer science term for this is a hash table, and in C#, it's called a Dictionary. You use it like this:
var peopleByName = new Dictionary<string, Person>();
string name = Console.ReadLine();
Person person = new Person(name);
peopleByName[name] = person;
Person theSamePerson = peopleByName[name];
theSamePerson, which was obtained by asking peopleByName for the object that is tied to the value of the name variable, will now refer to the same object that was added to the dictionary under that name.
I suspect that you are looking for Dictionary<TK,TV>. You can use a dictionary to map from a string to any object you like:
// create the dictionary to hold your records.
var records = new Dictionary<string,AddressRecord>();
var item = new AddressRecord("Mary", "Smith", "1234 N Park Ln", "Springfield", "OH");
var key = item.FirstName + " " + item.LastName;
records.Add(key, item);
// try to find someone by name
AddressRecord record;
var key = "Mary Smith";
if(records.TryGetValue(key, out record)) {
// use the record
Console.WriteLine("The address for "+ key + " is " + record.Address);
} else {
Console.WriteLine("No record found for " + key);
}
// or iterate over all the records:
foreach(var record in records.Values) {
Console.WriteLine(record.FirstName + " " record.LastName);
}
Of course the dictionary requires that all of it's keys are unique, so you might have problems if you know more than one person named Jon Smith.
I have looked for solutions and they all suggest to have a Constructor that takes a name and assigns it but I already have that and it is not what I want.
Can you explain why this isn't what you want? It is common and natural in .NET to give names to objects in this way. You could just write your Person class like this:
class Person
{
private string _name;
public Person(string input)
{
_name = input;
}
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
}
...and then access the object's name by calling its Person.Name property:
var somePerson = new Person("Bob");
Console.WriteLine(somePerson.Name);
In fact, this basically is how individual controls in Windows Forms are assigned names.
Furthermore, assuming your AdressBook variable is declared as a List<Person>, then you can access an individual person's name like this:
// Get the name of the third person added to the AdressBook list.
Console.WriteLine(AdressBook[2].Name);
If for some reason you're not making clear you want to store each Person object's name separately from the object itself, then the Dictionary<string, Person> approach mentioned in Aasmund's answer is perfectly fine. Another option to explore if you really want to stick with the List container type could maybe be a List<Tuple<string, Person>> variable using the .NET Tuple(T1, T2) type.
Without more detail on your requirements, there are dozens of ways you could do this.

"An item with same key has already been added" not getting this exception if adding duplicate object as a key in dictionary?

I have just a silly doubt that below is my small piece of code.
IDictionary<string, string> dic = new Dictionary<string, string>();
IDictionary<Demo, string> dic2 = new Dictionary<Demo, string>();
dic.Add("S1", "S1.1");
dic.Add("S1", "S1.1");
Demo d1 = new Demo() { MyProperty = 1 };
dic2.Add(d1, "1");
d1 = new Demo() { MyProperty = 1 };
dic2.Add(d1, "2");
So my question is i am getting exception at line no 4 which is correct according to dictionary concept that I can not add duplicate key in Dictionary however when I am trying to do same thing with object and object with same name I am not getting any exception event Dictionary have now two object of same type and same name.
Please help to understand this concept.
When inserting data into dictionary, Equals is used to check wheter key is unique.
In string Equals is implemented to check its value. That is why you cannot insert two strings into dictionary. What's more string is strange type - technically it is reference type, but acts like value type.
In bare object (or when not overriden), Equals is implemented to compare object by reference - it just checks wheter variabled points to the same area of memory (note: we are talking about classes, not structs :-)). So when you create two objects:
Demo d1 = new Demo() { MyProperty = 1 };
d1 = new Demo() { MyProperty = 1 };
There are two copies of that object in different place in memory, so Equals says: "That objects are different" - and you can add it to the dictionary.
You can of course change this behaviour by overriding Equals (and GetHashCode but here is skipped to keep answer clear) in Demo:
public override bool Equals(object obj)
{
// If parameter cannot be cast to Demo or is null return false.
Demo p = obj as Demo;
if (p == null)
{
return false;
}
// Return true if the fields match:
return (MyPropertyx == p.MyProperty));
}
Then:
Demo d1 = new Demo() { MyProperty = 1 };
dic2.Add(d1, "1");
d1 = new Demo() { MyProperty = 1 };
dic2.Add(d1, "2");
will not definitely allow to insert second value.
In C# strings behave like value types which means that the value of the string is compared (which is called Value Equality), meaning:
var string1 = "S1";
var string2 = "S1";
Console.WriteLine(string1 == string2) // will output true
In the case of objects (such as your Demo object) by default when they are compared they are only checked to see if they are the same object (which is called Reference Equality), meaning:
var d1 = new Demo() { MyProperty = 1 };
var d2 = new Demo() { MyProperty = 1 };
Console.WriteLine(d1 == d2) // will output false
Now with objects you can implement Value Equality so that the above example will output true.
You can learn more about that with this guideline.
You have two objects using the same variable name (d1) but they refer to different instances of an object - so there is no duplicate.
It also looks like you expect both objects to be the "same" based on both having a property MyProperty with the value 1. This is not the case - the dictionary, by default, will use the references as the key, which are different as I mentioned above. To enable the dictionary to treat your two objects as the same you would need to override Equals and GetHashCode in your class Demo

How can i access Structure property that is inside Hashtable? [duplicate]

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; }
};

Returning an object based on type

I have 5 different classes that all inherit from BaseEntity. I would like to create a new model class that will store information needed about one of these 5 classes as well as other identifiers.
When I retrieve the data for this new model from the database, all I get is a string with the class type along with an integer that represents which entry I can reference from the database.
For example, if I retrieve Id = 2, Type = "BaseBall". That means I will have need to use my BaseBallService to fetch the entry where Id == 2. If it happens to be Id = 2, Type = "BasketBall", then I will use BasketBallService.
Currently the only solution I can think of is it to have a bunch of if statements that evaluate the 'type' string. Depending on if the type matches a valid type (BaseBall, FootBall, BasketBall, etc.) then that object is returned.
Is there a way to easily do this without the need to define all 5 types in the model definition and stringing if or statements to identify this?
I hope I have identified the problem clearly enough. Let me know if any additional information is needed. I haven't written any code for this yet. I am merely trying to analyze the problem and form a solution.
I would just add a global enum at the project or solution level to store types. That way if you wish to add to it later you may without breaking any existing code as it is detached. But this may keep it well typed and thus demand a type that is listed from the end user or application. I did a simple console app to show this. You may apply the enum to any class not just a generic though. I also implement a return method to narrow down the return lists to show how I can get lists of my lists easier.
public enum types
{
Type1,
Type2,
Type3
}
public class GenericListing
{
public string Description { get; set; }
public types Type { get; set; }
}
class Program
{
public static List<GenericListing> GetTypeListing(List<GenericListing> aListings, types aTypes)
{
return aListings.Where(x => x.Type == aTypes).ToList();
}
static void Main(string[] args)
{
var stuff = new List<GenericListing>
{
new GenericListing {Description = "I am number 1", Type = types.Type1},
new GenericListing {Description = "I am number 2", Type = types.Type2},
new GenericListing {Description = "I am number 3", Type = types.Type3},
new GenericListing {Description = "I am number 1 again", Type = types.Type1},
};
string s = "";
GetTypeListing(stuff, types.Type1) // Get a specific type but require a well typed input.
.ForEach(n => s += n.Description + "\tType: " + n.Type + Environment.NewLine);
Console.WriteLine(s);
Console.ReadLine();
}
}
You may try using Dictionary, e.g.
Dictionary<String, BaseEntry> types = new Dictionary<String, BaseEntry>() {
{"BaseBall", new BaseBallService()},
{"BasketBall", new BasketBallService()},
...
}
...
var value = types["BaseBall"].GetId(2);

Categories

Resources