Reputation: 188
I am facing a problem where I want to create an instance of a SomeClass<T>
where T
is giving by the method, but SomeClass<T>
is added to a dictionary that is declared like ConcurrentDictionary<OtherClass, SomeClass<object>>
. What I want to do does not compile because Type 'T' doesn't match expected type object
private readonly SemaphoreSlim semaphoreSlim;
private readonly ConcurrentDictionary<StatisticType, StatisticsList<object>> statisticsDictionary;
public StatisticsDictionary()
{
semaphoreSlim = new SemaphoreSlim(1);
statisticsDictionary = new ConcurrentDictionary<StatisticType, StatisticsList<object>>();
}
(...)
public bool TryAdd<T>(StatisticType key, DateTime dateTime, T value)
{
if (!statisticsDictionary.ContainsKey(key))
{
semaphoreSlim.Wait();
statisticsDictionary.TryAdd(key, new StatisticsList<T>());
semaphoreSlim.Release();
}
if (!statisticsDictionary.TryGetValue(key, out var statistics))
return false;
statistics.Add(dateTime, value);
return true;
}
The concurrentDictionary is wrapped in a class that needs to support different types of 'T' (like long, DateTime, TimeSpan and possibly other types)
I've tried to just initialize SomeClass<T>
as SomeClass<object>
so that it matches the declaration. And then on retrieval cast object
to T
that is given by the method. But this leads to casting exceptions because it seems to be impossible to (explicitly) cast an object to, for example, long even when the object is in fact a long during run time.
public bool TryGetFrom<T>(StatisticType key, DateTime from, out IEnumerable<(DateTime, T)> value)
{
if (statisticsDictionary.TryGetValue(key, out var statistics))
{
var list = statistics.TryGetFrom(from);
value = (IEnumerable<(DateTime, T)>) list;
return value != null;
}
value = null;
return false;
}
Is it even possible what I try to accomplish?
Edit Source code of StatisticsList as requested in the comments
public class StatisticsList<T>
{
private readonly SemaphoreSlim semaphoreSlim;
private readonly SortedList<DateTime, (DateTime, T)> sortedList;
public DateTime StartedAt { get; }
public int Count => sortedList.Count;
public StatisticsList()
{
try
{
semaphoreSlim.Wait();
sortedList.Add(key, (key, value));
}
finally
{
semaphoreSlim.Release();
}
}
public void Add(DateTime key, T value)
{
semaphoreSlim.Wait();
sortedList.Add(key, (key, value));
semaphoreSlim.Release();
}
public bool TryGetFrom(DateTime from, out IEnumerable<(DateTime, T)> value)
{
try
{
semaphoreSlim.Wait();
value = sortedList.Where(s => s.Key > from).Select(s => s.Value).ToList();
return true;
}
catch (ArgumentNullException) { }
finally
{
semaphoreSlim.Release();
}
value = null;
return false;
}
public bool TryGetLast(out (DateTime, T) value)
{
try
{
semaphoreSlim.Wait();
var dateTime = sortedList.Max(s => s.Key);
return sortedList.TryGetValue(dateTime, out value);
}
catch (ArgumentNullException)
{
value = (default, default);
}
finally
{
semaphoreSlim.Release();
}
return false;
}
public IEnumerable<(DateTime, T)> ToList()
{
try
{
semaphoreSlim.Wait();
return sortedList.Values.ToList();
}
finally
{
semaphoreSlim.Release();
}
}
}
Upvotes: 1
Views: 476
Reputation: 34189
Generics are effective when you know the type during design-time. In your case, your collection can store items of any different types.
You can utilize a pretty popular trick of declaring non-generic and inherited generic version of a collection item. As a side effect, you can get rid of a not-very-readable value tuple :)
Declare item class like this:
public abstract class StatisticsListItem
{
public DateTime Time { get; protected set; }
}
public class StatisticsListItem<T> : StatisticsListItem
{
public StatisticsListItem(DateTime time, T value)
{
Time = time;
Value = value;
}
public T Value { get; }
}
And then you can store them as StatisticsListItem
and perform operations depending on their specific sub-type. It is also type-safe, because you cannot access the value of a generic list item, like in case if you declare it as non-generic StatisticsListItem
with object Value { get; }
property.
The simplest usage scenario can look like this:
public class StatisticsList
{
public List<StatisticsListItem> Values { get; set; } = new List<StatisticsListItem>();
public void TryAdd<T>(DateTime dateTime, T value)
{
// Adding an item of generic type to non-generic list:
Values.Add(new StatisticsListItem<T>(dateTime, value));
}
public IEnumerable<StatisticsListItem<T>> TryGetFrom<T>(DateTime dateTime)
{
// Dynamically filtering only items of specific generic type
return Values
.OfType<StatisticsListItem<T>>() // computationally ineffective, subject to improvement ;)
.Where(t => t.Time >= dateTime); // same, just an example
}
}
It is just an example. You still need to implement it in a way it supports keys, fits into your dictionary and makes filtering of items by type much more effective. Good luck!
Some minor comments: you still might have some problems with handling multi-threading, but this is out of the scope of this question :)
Upvotes: 2