I have the following code
Dictionary<string, string> changesDictionary = new Dictionary<string, string>();
if (changesDictionary.ContainsKey("field1"))
{
resultObject.field1 = changesDictionary["field1"];
}
if (changesDictionary.ContainsKey("field2"))
{
resultObject.field2 = changesDictionary["field2"];
}
if (changesDictionary.ContainsKey("field3"))
{
resultObject.field3 = changesDictionary["field3"];
}
which has 4 lines for a potential assignment. I'm wondering if there is a way to write it shorter.
I've tried the ternary operator which makes one line but it's harder to read.
resultObject.field1 = changesDictionary.ContainsKey("field1") ? changesDictionary["field1"] : resultObject.field1;
You could always do something like this. It's more verbose to start, but if you have lots of properties then it might pay off:
var fields = new (string key, Action<ResultObject, string> setter)[]
{
("field1", (x, val) => x.field1 = val),
("field2", (x, val) => x.field2 = val),
("field3", (x, val) => x.field3 = val),
};
foreach (var (key, setter) in fields)
{
if (changesDictionary.TryGetValue(key, out var field))
{
setter(resultObject, field);
}
}
Another option is something like this:
// A local function which captures 'resultObject' and 'changesDictionary'
void Set(string key, Action<ResultObject, string> setter)
{
if (changesDictionary.TryGetValue(key, out var field))
{
setter(resultObject, field);
}
}
Set("field1", (x, val) => x.field1 = val);
Set("field2", (x, val) => x.field2 = val);
Set("field3", (x, val) => x.field3 = val);
Otherwise, if you're prepared to change your style slightly, you can do this:
if (changesDictionary.TryGetValue("field1", out var field1)) resultObject.field1 = field1;
if (changesDictionary.TryGetValue("field2", out var field2)) resultObject.field2 = field2;
if (changesDictionary.TryGetValue("field3", out var field3)) resultObject.field1 = field3;
Using a local function:
void SetField(string fieldName, Action<string> updater)
{
if (changesDictionary.TryGetValue(fieldName, out string fieldValue))
{
updater(fieldValue);
}
}
SetField("field1", f => resultObject.field1 = f);
SetField("field2", f => resultObject.field2 = f);
SetField("field3", f => resultObject.field3 = f);
Price to pay = readability--
Line count = 11 instead of 13
Using a local function + reflection (provided fieldx are public properties):
void SetField(string fieldName)
{
if (changesDictionary.TryGetValue(fieldName, out string fieldValue))
{
PropertyInfo propertyInfo = resultObject.GetType().GetProperty(fieldName);
propertyInfo.SetValue(resultObject, fieldValue);
}
}
SetField("field1");
SetField("field2");
SetField("field3");
Price to pay = performance--
Line count = 12 instead of 13, but if you have 20 fields to update:
for (int i = 1; i <= 20; i++)
{
SetField($"field{i}");
}
Much shorter
Assuming (given the lowercase names for the field fields) that field1, field2 and field3 are actually fields rather than properties, then you can write a local function to simplify the code as follows:
Dictionary<string, string> changesDictionary = new Dictionary<string, string>();
void update(ref string field, string key)
{
if (changesDictionary.TryGetValue(key, out var value))
field = value;
}
update(ref resultObject.field1, "field1");
update(ref resultObject.field2, "field1");
update(ref resultObject.field3, "field1");
Note that will NOT work if field1 etc are actually properties, because of course you can't use ref with a property.
public static class DictionaryExtensions
{
public static TValue GetOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
if (dictionary.TryGetValue(key, out value))
return value;
return defaultValue;
}
}
. . .
resultObject.field1 = changesDictionary.GetOrDefault("field1", resultObject.field1);
resultObject.field2 = changesDictionary.GetOrDefault("field2", resultObject.field2);
resultObject.field3 = changesDictionary.GetOrDefault("field3", resultObject.field3);
If your object has FIELDS not PROPERTIES, u can use just TryGetValue to field like this
changesDictionary.TryGetValue(nameof(ResultObject.field1), out resultObject.field1);
full example:
using System;
using System.Collections.Generic;
namespace ConsoleApp27
{
internal class Program
{
private static void Main(string[] args)
{
var resultObject = new ResultObject();
var changesDictionary = new Dictionary<string, string>();
changesDictionary.Add(nameof(ResultObject.field1), "q1");
changesDictionary.Add(nameof(ResultObject.field2), "q2");
changesDictionary.Add(nameof(ResultObject.field3), "q3");
changesDictionary.Add(nameof(ResultObject.field4), "q4");
changesDictionary.TryGetValue(nameof(ResultObject.field1), out resultObject.field1);
changesDictionary.TryGetValue(nameof(ResultObject.field2), out resultObject.field2);
changesDictionary.TryGetValue(nameof(ResultObject.field3), out resultObject.field3);
changesDictionary.TryGetValue(nameof(ResultObject.field4), out resultObject.field4);
Console.WriteLine(resultObject.field1);
Console.WriteLine(resultObject.field2);
Console.WriteLine(resultObject.field3);
Console.WriteLine(resultObject.field4);
Console.ReadLine();
}
public class ResultObject
{
public string field1;
public string field2;
public string field3;
public string field4;
}
}
}
output:
q1
q2
q3
q4
Related
I want to be able to query and sort a Dictionary<int, MyObj> by any of the properties inside MyObj.
class MyObj
{
public string Symbol { get; set; }
public string Name { get; set; }
public int AtomicNumber { get; set; }
public int Id { get; set; }
public string toString()
{
return Symbol + " " + Name + " " + AtomicNumber + " " + Id;
}
}
class Program
{
private static void AddToDictionary(Dictionary<int, MyObj> elements,
string symbol, string name, int atomicNumber, int id)
{
MyObj theElement = new MyObj();
theElement.Symbol = symbol;
theElement.Name = name;
theElement.AtomicNumber = atomicNumber;
theElement.Id = id;
elements.Add(key: theElement.Id, value: theElement);
}
private static Dictionary<int, MyObj> BuildDictionary()
{
var elements = new Dictionary<int, MyObj>();
AddToDictionary(elements, "K", "Potassium", 19, 0);
AddToDictionary(elements, "Ca", "Calcium", 20, 1);
AddToDictionary(elements, "Sc", "Scandium", 21, 2);
AddToDictionary(elements, "Ti", "Titanium", 22, 3);
return elements;
}
static List<T> GetListOfProperty<T>(Dictionary<int, MyObj> colBlobs, string property)
{
Type t = typeof(MyObj);
PropertyInfo prop = t.GetProperty(property);
if (prop == null)
{
// throw new Exception(string.Format("Property {0} not found", f.Name.ToLower()));
Console.WriteLine(string.Format("Property {0} not found", property));
return new List<T>();
}
//still need to fix this
return colBlobs.Values
.Select(blob => (T)prop.GetValue(blob))
.OrderBy(x => x)
.ToList();
}
static SortedDictionary<int, MyObj> GetListOfProperty2<T>(Dictionary<int, MyObj> colBlobs, string property)
{
// CODE?
return sortedDict;
}
static void Main(string[] args)
{
Dictionary<int, MyObj> myColl = BuildDictionary();
var res = GetListOfProperty<string>(myColl, "Name");
foreach (var rr in res)
Console.WriteLine(rr.ToString());
//outputs : Which is only one property, the one selected
//--------
//Calcium
//Potassium
//Scandium
//Titanium
var res2 = GetListOfProperty2<string>(myColl, "Name");
//want to output the whole dictionary
//<1, {"Ca", "Calcium", 20,1}
//<0, {"K", "Potassium", 19, 0}
//<2, {"Sc", "Scandium", 21, 2}
//<3, {"Ti", "Titanium", 22, 3}
}
}
Since it seems to be that it is unclear what I want. I added example output. I am pretty sure there is no way to make this question more clear.
The problem with SortedDictionary is that it can only be sorted by Key, so you'll have to use OrderBy() in some way:
public static IOrderedEnumerable<KeyValuePair<K, T>> SortByMember<K, T>(this Dictionary<K, T> data, string memberName)
{
Type type = typeof(T);
MemberInfo info = type.GetProperty(memberName) ?? type.GetField(memberName) as MemberInfo;
Func<KeyValuePair<K, T>, object> getter = kvp => kvp.Key;
if (info is PropertyInfo pi)
getter = kvp => pi.GetValue(kvp.Value);
else if (info is FieldInfo fi)
getter = kvp => fi.GetValue(kvp.Value);
return data.OrderBy(getter);
}
This can handle both properties and fields, and if the member name is invalid, it defaults to sorting by key.
You can change that to not sort if member name is invalid by changing the return value:
public static IEnumerable<KeyValuePair<K, T>> SortByMember<K, T>(this Dictionary<K, T> data, string memberName)
{
Type type = typeof(T);
MemberInfo info = type.GetProperty(memberName) ?? type.GetField(memberName) as MemberInfo;
if (info == null) return data;
Func<KeyValuePair<K, T>, object> getter = null;
if (info is PropertyInfo pi)
getter = kvp => pi.GetValue(kvp.Value);
else if (info is FieldInfo fi)
getter = kvp => fi.GetValue(kvp.Value);
return data.OrderBy(getter);
}
IMO it is wrong to return an empty dictionary if it fails to find the member. Alternatively you can throw an exception.
Use case:
Dictionary<int, MyObj> myColl = BuildDictionary();
var res = myColl.SortByMember("Name");
foreach (var rr in res)
Console.WriteLine(rr.Value);
To achieve your goal you can use expression tree to build a compiled lambda and then use this lambda in the Linq OrderBy() method:
public static class ExpressionHelper
{
public static Func<T, object> GetMemberExpressionFunc<T>(string memberName)
{
var parameter = Expression.Parameter(typeof(T));
Expression source = Expression.PropertyOrField(parameter, memberName);
Expression conversion = Expression.Convert(source, typeof(object));
return Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
}
}
static void Main(string[] args)
{
Dictionary<int, MyObj> myColl = BuildDictionary();
string property = "AtomicNumber"; // or whatever property you want your dictionary ordered by
var func = ExpressionHelper.GetMemberExpressionFunc<MyObj>(property);
var ordered = myColl.OrderBy(x => func(x.Value));
}
I have a list public List<ArticleWarehouseLocations> ArticleWarehouseLocationsList. In this list I have a property called Position.
`Swap<long>(ref ArticleWarehouseLocationsList[currentIndex].Position, ref ArticleWarehouseLocationsList[currentIndex - 1].Position);`
public void Swap<T>(ref T lhs, ref T rhs)
{
T temp = lhs;
lhs = rhs;
rhs = temp;
}
I'm trying to do something like this. It's giving me an error property or index may not be passed as ref or out.
I can use a local variable and assign it the value and use it but I'm looking for a global solution.
What you can do is make the property return by reference:
class Obj {
private long pos;
public ref long Position { get { return ref pos; } }
}
static void Main(string[] args)
{
Obj[] arr = new Obj[2] { new Obj(), new Obj() };
arr[0].Position = 10;
arr[1].Position = 20;
int index = 0;
WriteLine($"{arr[index].Position}, {arr[index+1].Position}");
Swap<long>(ref arr[index].Position, ref arr[index+1].Position);
WriteLine($"{arr[index].Position}, {arr[index+1].Position}");
}
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/ref-returns
I believe the tuple swap (x, y) = (y, x) proposed in comments is the way to go, but wanted to share yet another aproach with LINQ Expressions (a bit too long for a comment, so posting as an answer)
public static void SwapProperties<T>(T lhs, T rhs, Expression<Func<T, object>> propExpression)
{
var prop = GetPropertyInfo(propExpression);
var lhsValue = prop.GetValue(lhs);
var rhsValue = prop.GetValue(rhs);
prop.SetValue(lhs, rhsValue);
prop.SetValue(rhs, lhsValue);
}
private static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> propExpression)
{
PropertyInfo prop;
if (propExpression.Body is MemberExpression memberExpression)
{
prop = (PropertyInfo) memberExpression.Member;
}
else
{
var op = ((UnaryExpression) propExpression.Body).Operand;
prop = (PropertyInfo) ((MemberExpression) op).Member;
}
return prop;
}
class Obj
{
public long Position { get; set; }
public string Name { get; set; }
}
public static void Main(string[] args)
{
var a1 = new Obj()
{
Position = 10,
Name = "a1"
};
var a2 = new Obj()
{
Position = 20,
Name = "a2"
};
SwapProperties(a1, a2, obj => obj.Position);
SwapProperties(a1, a2, obj => obj.Name);
Console.WriteLine(a1.Position);
Console.WriteLine(a2.Position);
Console.WriteLine(a1.Name);
Console.WriteLine(a2.Name);
}
Given a city:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public LatLong Location { get; set; }
}
I have a list of close to 3,000,000 cities (and towns and villages etc.) in a file. This file is read into memory; I have been playing with arrays, lists, dictionaries (key = Id) etc.
I want to find, as quick as possible, all cities matching a substring (case insensitive). So when I search for 'yor' I want to get all matches (1000+) ASAP (matching 'York Town', 'Villa Mayor', 'New York', ...).
Functionally you could write this as:
cities.Values.Where(c => c.Name.IndexOf("yor", StringComparison.OrdinalIgnoreCase) >= 0)
I don't mind doing some pre-processing when reading the file; as a matter of fact: that's what I'm mostly looking for. Read the file, "chew" on the data creating some sort of index or... and then be ready to answer queries like "yor".
I want this to be standalone, self-contained. I do not want to add dependencies like an RDBMS, ElasticSearch or whatever. I don't mind having (parts of) the list in memory more than once. I don't mind spending some memory on a datastructure to help me find my results quickly. I don't want libraries or packages. I want an algorithm I can implement myself.
Basically I want the above LINQ statement, but optimized for my case; currently plowing through almost 3,000,000 records takes about +/- 2 seconds. I want this sub 0.1 second so I could use the search and it's results as 'autocomplete'.
Creating an "index"(-alike) structure is probably what I need. As I'm writing I remember something about a "bloom filter" but I'm not sure if that would help or even supports substring search. Will look into that now.
Any tips, pointers, help very much appreciated.
I created a bit of a hybrid based on a suffix array / dictionary. Thanks to saibot for suggesting it first and all other people helping and suggesting.
This is what I came up with:
public class CitiesCollection
{
private Dictionary<int, City> _cities;
private SuffixDict<int> _suffixdict;
public CitiesCollection(IEnumerable<City> cities, int minLen)
{
_cities = cities.ToDictionary(c => c.Id);
_suffixdict = new SuffixDict<int>(minLen, _cities.Values.Count);
foreach (var c in _cities.Values)
_suffixdict.Add(c.Name, c.Id);
}
public IEnumerable<City> Find(string find)
{
var normalizedFind = _suffixdict.NormalizeString(find);
foreach (var id in _suffixdict.Get(normalizedFind).Where(v => _cities[v].Name.IndexOf(normalizedFind, StringComparison.OrdinalIgnoreCase) >= 0))
yield return _cities[id];
}
}
public class SuffixDict<T>
{
private readonly int _suffixsize;
private ConcurrentDictionary<string, IList<T>> _dict;
public SuffixDict(int suffixSize, int capacity)
{
_suffixsize = suffixSize;
_dict = new ConcurrentDictionary<string, IList<T>>(Environment.ProcessorCount, capacity);
}
public void Add(string suffix, T value)
{
foreach (var s in GetSuffixes(suffix))
AddDict(s, value);
}
public IEnumerable<T> Get(string suffix)
{
return Find(suffix).Distinct();
}
private IEnumerable<T> Find(string suffix)
{
foreach (var s in GetSuffixes(suffix))
{
if (_dict.TryGetValue(s, out var result))
foreach (var i in result)
yield return i;
}
}
public string NormalizeString(string value)
{
return value.Normalize().ToLowerInvariant();
}
private void AddDict(string suffix, T value)
{
_dict.AddOrUpdate(suffix, (s) => new List<T>() { value }, (k, v) => { v.Add(value); return v; });
}
private IEnumerable<string> GetSuffixes(string value)
{
var nv = NormalizeString(value);
for (var i = 0; i <= nv.Length - _suffixsize ; i++)
yield return nv.Substring(i, _suffixsize);
}
}
Usage (where I assume mycities to be an IEnumerable<City> with the given City object from the question):
var cc = new CitiesCollection(mycities, 3);
var results = cc.Find("york");
Some results:
Find: sterda elapsed: 00:00:00.0220522 results: 32
Find: york elapsed: 00:00:00.0006212 results: 155
Find: dorf elapsed: 00:00:00.0086439 results: 6095
Memory usage is very, very acceptable. Only 650MB total having the entire collection of 3,000,000 cities in memory.
In the above I'm storing Id's in the "SuffixDict" and I have a level of indirection (dictionary lookups to find id=>city). This can be further simplified to:
public class CitiesCollection
{
private SuffixDict<City> _suffixdict;
public CitiesCollection(IEnumerable<City> cities, int minLen, int capacity = 1000)
{
_suffixdict = new SuffixDict<City>(minLen, capacity);
foreach (var c in cities)
_suffixdict.Add(c.Name, c);
}
public IEnumerable<City> Find(string find, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase)
{
var normalizedFind = SuffixDict<City>.NormalizeString(find);
var x = _suffixdict.Find(normalizedFind).ToArray();
foreach (var city in _suffixdict.Find(normalizedFind).Where(v => v.Name.IndexOf(normalizedFind, stringComparison) >= 0))
yield return city;
}
}
public class SuffixDict<T>
{
private readonly int _suffixsize;
private ConcurrentDictionary<string, IList<T>> _dict;
public SuffixDict(int suffixSize, int capacity = 1000)
{
_suffixsize = suffixSize;
_dict = new ConcurrentDictionary<string, IList<T>>(Environment.ProcessorCount, capacity);
}
public void Add(string suffix, T value)
{
foreach (var s in GetSuffixes(suffix, _suffixsize))
AddDict(s, value);
}
public IEnumerable<T> Find(string suffix)
{
var normalizedfind = NormalizeString(suffix);
var find = normalizedfind.Substring(0, Math.Min(normalizedfind.Length, _suffixsize));
if (_dict.TryGetValue(find, out var result))
foreach (var i in result)
yield return i;
}
private void AddDict(string suffix, T value)
{
_dict.AddOrUpdate(suffix, (s) => new List<T>() { value }, (k, v) => { v.Add(value); return v; });
}
public static string NormalizeString(string value)
{
return value.Normalize().ToLowerInvariant();
}
private static IEnumerable<string> GetSuffixes(string value, int suffixSize)
{
var nv = NormalizeString(value);
if (value.Length < suffixSize)
{
yield return nv;
}
else
{
for (var i = 0; i <= nv.Length - suffixSize; i++)
yield return nv.Substring(i, suffixSize);
}
}
}
This bumps the load time up from 00:00:16.3899085 to 00:00:25.6113214, memory usage goes down from 650MB to 486MB. Lookups/searches perform a bit better since we have one less level of indirection.
Find: sterda elapsed: 00:00:00.0168616 results: 32
Find: york elapsed: 00:00:00.0003945 results: 155
Find: dorf elapsed: 00:00:00.0062015 results: 6095
I'm happy with the results so far. I'll do a little polishing and refactoring and call it a day! Thanks everybody for the help!
And this is how it performs with 2,972,036 cities:
This has evolved into a case-insensitive, accent-insensitive search by modifying the code to this:
public static class ExtensionMethods
{
public static T FirstOrDefault<T>(this IEnumerable<T> src, Func<T, bool> testFn, T defval)
{
return src.Where(aT => testFn(aT)).DefaultIfEmpty(defval).First();
}
public static int IndexOf(this string source, string match, IEqualityComparer<string> sc)
{
return Enumerable.Range(0, source.Length) // for each position in the string
.FirstOrDefault(i => // find the first position where either
// match is Equals at this position for length of match (or to end of string) or
sc.Equals(source.Substring(i, Math.Min(match.Length, source.Length - i)), match) ||
// match is Equals to on of the substrings beginning at this position
Enumerable.Range(1, source.Length - i - 1).Any(ml => sc.Equals(source.Substring(i, ml), match)),
-1 // else return -1 if no position matches
);
}
}
public class CaseAccentInsensitiveEqualityComparer : IEqualityComparer<string>
{
private static readonly CompareOptions _compareoptions = CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreSymbols;
private static readonly CultureInfo _cultureinfo = CultureInfo.InvariantCulture;
public bool Equals(string x, string y)
{
return string.Compare(x, y, _cultureinfo, _compareoptions) == 0;
}
public int GetHashCode(string obj)
{
return obj != null ? RemoveDiacritics(obj).ToUpperInvariant().GetHashCode() : 0;
}
private string RemoveDiacritics(string text)
{
return string.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
}
public class CitiesCollection
{
private SuffixDict<City> _suffixdict;
private HashSet<string> _countries;
private Dictionary<int, City> _cities;
private readonly IEqualityComparer<string> _comparer = new CaseAccentInsensitiveEqualityComparer();
public CitiesCollection(IEnumerable<City> cities, int minLen, int capacity = 1000)
{
_suffixdict = new SuffixDict<City>(minLen, _comparer, capacity);
_countries = new HashSet<string>();
_cities = new Dictionary<int, City>(capacity);
foreach (var c in cities)
{
_suffixdict.Add(c.Name, c);
_countries.Add(c.Country);
_cities.Add(c.Id, c);
}
}
public City this[int index] => _cities[index];
public IEnumerable<string> Countries => _countries;
public IEnumerable<City> Find(string find, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase)
{
foreach (var city in _suffixdict.Find(find).Where(v => v.Name.IndexOf(find, _comparer) >= 0))
yield return city;
}
}
public class SuffixDict<T>
{
private readonly int _suffixsize;
private ConcurrentDictionary<string, IList<T>> _dict;
public SuffixDict(int suffixSize, IEqualityComparer<string> stringComparer, int capacity = 1000)
{
_suffixsize = suffixSize;
_dict = new ConcurrentDictionary<string, IList<T>>(Environment.ProcessorCount, capacity, stringComparer);
}
public void Add(string suffix, T value)
{
foreach (var s in GetSuffixes(suffix, _suffixsize))
AddDict(s, value);
}
public IEnumerable<T> Find(string suffix)
{
var find = suffix.Substring(0, Math.Min(suffix.Length, _suffixsize));
if (_dict.TryGetValue(find, out var result))
{
foreach (var i in result)
yield return i;
}
}
private void AddDict(string suffix, T value)
{
_dict.AddOrUpdate(suffix, (s) => new List<T>() { value }, (k, v) => { v.Add(value); return v; });
}
private static IEnumerable<string> GetSuffixes(string value, int suffixSize)
{
if (value.Length < 2)
{
yield return value;
}
else
{
for (var i = 0; i <= value.Length - suffixSize; i++)
yield return value.Substring(i, suffixSize);
}
}
}
With credit also to Netmage and Mitsugui. There are still some issues / edge-cases but it's continually improving!
You could use a suffix tree: https://en.wikipedia.org/wiki/Suffix_tree
It requires enough space to store about 20 times your list of words in memory
Suffix array is a space efficient alternative: https://en.wikipedia.org/wiki/Suffix_array
in query benchmark contains very faster then indexOf >0
cities.Values.Where(c => c.Name.Contans("yor"))
Not sure how to best phrase this which is probably why I'm having difficulty looking it up. Here is a sample console application to demonstrate my meaning.
class Program
{
static void Main(string[] args)
{
var item1 = new Item("Number");
var item2 = new Item("Number");
var dict = new Dictionary<Item, string>();
dict.Add(item1, "Value");
Console.WriteLine(dict.ContainsKey(item2));
var dict2 = new Dictionary<string, string>();
dict2.Add("Number", "Value");
Console.WriteLine(dict2.ContainsKey("Number"));
Console.Read();
}
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
}
}
In this example dict.ContainsKey(item2) returns false and dict2.ContainsKey("Number") returns true. Can Item be defined in such a way that it would behave like a string? The best I can come up with is
static void Main(string[] args)
{
var item1 = new Item("Number");
var item2 = new Item("Number");
var dict = new Dictionary<string, string>();
dict.Add(item1.ToString(), "Test");
Console.WriteLine(dict.ContainsKey(item2.ToString()));
Console.Read();
}
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
public override string ToString()
{
return number;
}
}
This example is contrived, Item would have more fields and ToString() would joint them all up.
You need to override Equals and GetHashCode. Dictionary use Equals and GetHashCode method to compare keys for equality.
class Item
{
readonly string number;
public Item(string number)
{
this.number = number;
}
public override bool Equals(object obj)
{
return Equals(obj as Item);
}
public override int GetHashCode()
{
// this is c# 6 feature
return number?.GetHashCode() ?? 0;
// If you are not using c# 6, you can use
// return number == null ? 0 : number.GetHashCode();
}
private bool Equals(Item another)
{
if (another == null)
return false;
return number == another.number;
}
}
If you have more than one field, you need to account all fields in the Equals and GetHashCode method.
I have a lot of times thinking about converting example of class to Dictionary<String, String> where key is variable name(class field name) and value is variable current assigned value. So we have a simple class:
public class Student
{
public String field1;
public Int64 field2;
public Double field3;
public Decimal field4;
public String SomeClassMethod1()
{
...
}
public Boolean SomeClassMethod2()
{
...
}
public Int64 SomeClassMethod1()
{
...
}
}
How I expect it will look like:
static void Main(String[] args)
{
Student student = new Student(){field1 = "", field2 = 3, field3 = 3.0, field4 = 4.55m};
Dictionary<String, String> studentInDictionary = ConvertAnyToDictionary<Student>(student);
}
public Dictionary<String, String> ConvertAnyToDictionary<T>(T value) where T:class
{
...
}
Any ideas about how to make it real? Thx a lot for any advices.
EDIT1:
Expected result:
studentInDictionary[0] = KeyValuePair("field1", "");
studentInDictionary[1] = KeyValuePair("field2", (3).ToString());
studentInDictionary[2] = KeyValuePair("field3", (3.0).ToString());
studentInDictionary[3] = KeyValuePair("field4", (4.55m).ToString());
Here is how you can do it:
public static Dictionary<String, String> ConvertAnyToDictionary<T>(T value) where T : class {
var fields = typeof(T).GetFields();
var properties = typeof(T).GetProperties();
var dict1 = fields.ToDictionary(x => x.Name, x => x.GetValue(value).ToString());
var dict2 = properties.ToDictionary(x => x.Name, x => x.GetValue(value, null).ToString());
return dict1.Union(dict2).ToDictionary(x => x.Key, x=> x.Value);
}
Edit: I'm taking in count both fields and properties there. If you will only be using properties, you can just use dict2.
You might want to take a look at the BindingFlags argument received by GetFields() and GetProperties() methods.
var proInfos = student.GetType().GetProperties();
if(proInfos!=null)
{
Dictionary<string,string> dict= new Dictionary<string, string>();
foreach (var propertyInfo in proInfos)
{
var tv = propertyInfo.GetValue(currentObj, null);
if(tv!=null)
{
if(dict.ContainsKey(propertyInfo.Name))
continue;
dict.Add(propertyInfo.Name, tv.ToString());
}
}
}
You can either serialize using already existing Serializers (Xml or JSON) or you can go about it using reflection.
Here is an example of how to get fields with reflection:
Not getting fields from GetType().GetFields with BindingFlag.Default