I'm trying to write an extension method to insert data into a dictionary of dictionaries defined as follows:
items=Dictionary<long,Dictionary<int,SomeType>>()
What I have so far is:
public static void LeafDictionaryAdd<TKEY1,TKEY2,TVALUE>(this IDictionary<TKEY1,IDictionary<TKEY2,TVALUE>> dict,TKEY1 key1,TKEY2 key2,TVALUE value)
{
var leafDictionary =
dict.ContainsKey(key1)
? dict[key1]
: (dict[key1] = new Dictionary<TKEY2, TVALUE>());
leafDictionary.Add(key2,value);
}
but the compiler doesn't like it. The statement:
items.LeafDictionaryAdd(longKey, intKey, someTypeValue);
gives me a type inference error.
For the statement:
items.LeafDictionaryAdd<long, int, SomeType>(longKey, intKey, someTypeValue);
I get "...does not contain a definition for... and the best extension method overload has some invalid arguments.
What am I doing wrong?
Some inventive generic usage ;-p
class SomeType { }
static void Main()
{
var items = new Dictionary<long, Dictionary<int, SomeType>>();
items.Add(12345, 123, new SomeType());
}
public static void Add<TOuterKey, TDictionary, TInnerKey, TValue>(
this IDictionary<TOuterKey,TDictionary> data,
TOuterKey outerKey, TInnerKey innerKey, TValue value)
where TDictionary : class, IDictionary<TInnerKey, TValue>, new()
{
TDictionary innerData;
if(!data.TryGetValue(outerKey, out innerData)) {
innerData = new TDictionary();
data.Add(outerKey, innerData);
}
innerData.Add(innerKey, value);
}
Try to use a concrete type:
public static void LeafDictionaryAdd<TKEY1,TKEY2,TVALUE>(this IDictionary<TKEY1, Dictionary<TKEY2,TVALUE>> dict,TKEY1 key1,TKEY2 key2,TVALUE value)
see the Dictionary<TKEY2,TVALUE> instead of IDictionary<TKEY2,TVALUE>
I'm guessing that this is a covariance / contravariance issue. Your method signature is expecting an IDictionary of IDcitionaries, but you're passing it an IDictionary of Dictionary. Try using a concrete Dictionary instead in your method signature, for the inner Dictionary.
If you specify an IDictionary on your Parameter list for the Extension method,
then your items will not match that.
Either change your Extension to
public static void LeafDictionaryAdd<TKEY1,TKEY2,TVALUE>(
this IDictionary<TKEY1, Dictionary<TKEY2,TVALUE>> dict,
TKEY1 key1,
TKEY2 key2,
TVALUE value)
OR Try and cast your items to
((IDictionary<long, IDictionary<int, YourType>>)items).LeafDictionaryAdd(l, i, o);
Related
This question already has answers here:
In C# 7 is it possible to deconstruct tuples as method arguments
(6 answers)
Closed 1 year ago.
Hi I have a function that returns a tuple of 2 values and I want to put them into a one-line add as seen below:
Dictionary<string, string> test = new Dictionary<string, string>();
test.Add(intoDict());
private (string, string) intoDict()
{
return ("Key","Value");
}
I need this to be a one-line operation.
If, for whatever reason it absolutely has to be a oneliner, you can create an extension method to handle the tuple value:
public static class Extension
{
public static void Add<TKey, TValue>(this IDictionary<TKey, TValue> dic, ValueTuple<TKey, TValue> tuple)
{
dic[tuple.Item1] = tuple.Item2;
}
}
which can then be called like this:
static (string, string) intoDict()
{
return ("Key", "Value");
}
static void Main(string[] args)
{
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add(intoDict()); //added here
return;
}
You need to specify the key and value separately like below in Add method as it expects two parmeters, first key and second value :
var tupleResult = intoDict();
test.Add(tupleResult.Item1, tupleResult.Item2);
Consider using an extension method.
public static class DictionaryExtensions
{
public static void Add<T, U>(this IDictionary<T, U> Dictionary, (T, U) tuple)
{
Dictionary.TryAdd(tuple.Item1, tuple.Item2);
}
}
This extension method provides an .Add() to any IDictionary.
To use this simply do:
(string,string) myTuple = ( "Key", "Value");
dictionary.Add(myTuple);
This old answer suggests making an extension method for this purpose, but the answer is 9 years old, so C# is probably different since then, or I don't understand the implementation.
I am currently trying this:
public static void AddIfNotPresent(this IDictionary<TKey, TValue> dict, TKey key, TValue value)
{
if (!dict.ContainsKey(key))
{
dict.Add(value);
}
}
... but Visual Studio says "The type or namespace TKey cannot be found...", same for value... Why can't I add these arbitrary types to an extension method?
TKey and TValue should be type parameters of AddIfNotPresent and AddIfNotPresent should be defined in a static class.
void Main()
{
var dictionary = new Dictionary<string, string>();
dictionary.AddIfNotPresent("key", "value");
Console.WriteLine($"{dictionary.First().Key} = {dictionary.First().Value}");
// Output: key = value
}
public static class DictionaryExtensions
{
public static void AddIfNotPresent<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue value)
{
if (!dict.ContainsKey(key))
{
dict.Add(key, value);
}
}
}
Your AddIfNotPresent does not define those generic types/arguments (AddIfNotPresent<TKey, TValue>). That answer is missing those which is a typo.
I'm trying to implement an extension method for all dictionaries whose value is of a type that implement a certain interface.
In this case I would like to have a ToListSortedByValue() method that returns
List<KeyValuePair<string, IComparable>>
for any dictionary of type
Dictionary<string, IComparable>
that would be cool because it would allow me to use dictionaries instead of lists, but to be able to have them sorted when needed (for example when printing in files or at console).
This is what I tried, but it doesn't work, any idea why?
public static List<KeyValuePair<string, IComparable>> ToListSortedByValue(this Dictionary<string, IComparable> Dic)
{
return Dic.OrderBy(x => x.Value).ToList();
}
EDIT:
it's solved already, but for completeness sake this is the problem I got:
when trying to use the method I got the an error as if such method didn't exist. If instead of IComparable I use an actual comparable type, let's say int or a class implementing IComparable, than it would work.
Basically you need to make the method generic on the value type and then constrain that type to be IComparable<T>.
public static List<KeyValuePair<string, T>> ToListSortedByValue<T>(
this Dictionary<string, T> Dic) where T : IComparable<T>
{
return Dic.OrderBy(x => x.Value).ToList();
}
This has the added bonus of returning the values as there passed in type. You might even want to make the key type generic too so it's not limited to just string
public static List<KeyValuePair<TKey, TValue>> ToListSortedByValue<TKey, TValue>(
this Dictionary<TKey, TValue> Dic) where TValue : IComparable<TValue>
{
return Dic.OrderBy(x => x.Value).ToList();
}
You need to make your method generic, so that it extends your actual type instead of just IComparable:
public static List<KeyValuePair<string, T>> ToListSortedByValue<T>(this Dictionary<string, T> Dic) where T : IComparable<T>
I would like to define the following two functions:
void Map<T>(Func<T, string> mapper);
T Call<T>(string value);
Map needs to store the function that turns a string into a result of type T so that when the "Call" function is called with a type T and a string the appropriate function can be looked up and called.
I was thinking that map could store the function in a dictionary of type Dictionary<Type, Func<object, string>> and then Call could do the casting to the appropriate type but I'm unable to get that to work. Does anyone know how to achieve this?
The first type argument of Func is the input, the second the output: Func<in T, out TResult> -- so you need Func<string, T>.
(The MSDN reference here uses Func<string, string> a fair bit which is annoying.)
Also, the dictionary can't use the type argument T as that's different for each element in the dictionary. Rather, use the superclass of Func<T, TResult> which is Delegate.
This should work:
Dictionary<Type, Delegate> dictionary = new Dictionary<Type, Delegate>();
public void Map<T>(Func<string, T> mapper)
{
dictionary[typeof(T)] = mapper;
}
public T Call<T>(string value)
{
var func = dictionary[typeof(T)] as Func<string, T>;
return func.Invoke(value);
}
You can try and do something like this (there should be a better way, but I can't see it right now):
Dictinary<Type, object> _funcDict = ...;
void Map<T>(Func<T, string>mapper)
{
_funcDict[typeof(T)] = mapper;
}
T Call<T>(string value)
{
var func = (Func<T, string>)_funcDict[typeof(T)]
return func(value);
}
What I don't like, is having an object value type in the dictionary, but I'm not sure how you can avoid it.
I am building the following class to manage a dictionary.
public class EnumDictionary<TKey, TValue>
{
private Dictionary<TKey, TValue> _Dict;
public EnumDictionary(Dictionary<TKey, TValue> Dict)
{
this._Dict = Dict;
}
public TKey GetValue(TValue value)
{
foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
{
if (kvp.Value == value)
return kvp.Key;
}
throw new Exception("Undefined data type: " + value);
}
}
But I am getting an error "Operator '==' cannot be applied to operands of type 'TValue' and 'TValue'".
BTW, I am making this custom collection is because my dictionary has unique value, but I can't get key by value from a dictionary.
Any help is appreciated. Thank you.
Did you try using the Equals method?
if (kvp.Value.Equals(value))
I think this restriction is due to the fact that the == operator can't be used with all types. Take the following for instance:
struct Test
{
public int Value;
}
Given the above struct, the following code will not compile:
Test a, b;
a = b = new Test();
bool areEqual = a == b; // Operator '==' cannot be applied to
// operands of type 'Test' and 'Test'
However, all types have the Equals method, so calling that will work:
Test a, b;
a = b = new Test();
bool areEqual = a.Equals(b);
Fredrik is right; you need to use Equals as you can't presume to be able to use == for all types, since the operator isn't defined for every type.
Depending on your scenario, it might also make sense to add
where TValue : IEquatable<TValue>
as a generic type constraint on your class. The reason for this is that object.Equals accepts another object as a parameter, which means that if TValue is a value type it will be boxed. If it can be known to implement IEquatable<TValue>, on the other hand, then Equals can be resolved to IEquatable<TValue>.Equals*, which takes a TValue as a parameter and therefore won't require value types to be boxed.
I might also recommend that you rethink the internal structure of this class. As it currently stands, there's no reason you need this class at all, as you could easily add an extension method to IDictionary<TKey, TValue> to find a key by value via enumeration over the values. What I would do instead is store two dictionaries: a Dictionary<TKey, TValue> and a Dictionary<TValue, TKey>, so that two-way lookup is possible in O(1).
*By the way, in case you're curious, the reason you can't use IEquatable<T> (or any interface for that matter) to ensure that a type has implemented the == operator is that operators are static, and interfaces cannot provide static methods (and thus can't provide operators).
When you use generic comparsions I think you should implement a (x)CompareTo(Y) or a comparable class. Please correct me if im wrong.
you can use if (kvp.Value.Equals(value)) instead of ==.
Use the "where" condition on your generic types
class Dictionary<TKey,TVal>
where TKey: IComparable, IEnumerable
where TVal: MyI
{
public void Add(TKey key, TVal val)
{
}
}
from http://msdn.microsoft.com/en-us/library/6b0scde8%28VS.80%29.aspx
Don't create a new class. Create a extension method:
public static class DictionaryHelper
{
public static TKey GetKeyFromValue<TKey, TValue>(this IDictionary<TKey, TValue> instance, TValue value)
{
foreach (var kvp in instance)
{
if (kvp.Value.Equals(value))
return kvp.Key;
}
return default(TKey);
}
}
public class Example
{
public static void Main(string[] argv)
{
Dictionary<string, string> test = new Dictionary<string, string> { { "Mykey", "MyValue" }, { "Key1", "Value2" } };
string key = test.GetKeyFromValue("MyValue");
}
}
If you want this to be general purpose, then you will want the definition of equality to be configurable, just as it is in the dictionary for keys.
Have a property of type IEqualityComparer<TValue>, which is set in the constructor.
Then have a version of the constructor that makes the default EqualityComparer<TValue>.Default. This will work by calling Equals on the type in question.
public class EnumDictionary<TKey, TValue>
{
private Dictionary<TKey, TValue> _Dict;
private readonly IEqualityComparer<TValue> _cmp;
public EnumDictionary(Dictionary<TKey, TValue> Dict, IEqualityComparer<TValue> cmp)
{
this._Dict = Dict;
_cmp = cmp;
}
public EnumDictionary(Dictionary<TKey, TValue> Dict)
:this(Dict, IEqualityComparer<TValue>.Default){}
public TKey GetValue(TValue value)
{
foreach (KeyValuePair<TKey, TValue> kvp in _Dict)
{
if (cmp.Equals(kvp.Value, value))
return kvp.Key;
}
throw new Exception("Undefined data type: " + value);
}
}