繁体   English   中英

C#:我如何使用泛型创建一个实例,而它已被声明为 object

[英]C#: How do I create an instance with a generic while it has been declared as object

我面临一个问题,我想创建SomeClass<T>的实例,其中T由方法给出,但SomeClass<T>被添加到声明为ConcurrentDictionary<OtherClass, SomeClass<object>>的字典中。 我想做的事情没有编译,因为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;
}

concurrentDictionary 包装在 class 中,需要支持不同类型的“T”(如 long、DateTime、TimeSpan 和可能的其他类型)

我试图将SomeClass<T>初始化为SomeClass<object>以便它与声明匹配。 然后在检索时将object转换为方法给出的T 但这会导致转换异常,因为似乎不可能(明确地)将 object 转换为 long,例如,即使 object 实际上在运行期间很长。

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

甚至有可能我试图完成的事情吗?


根据评论中的要求编辑StatisticsList 的源代码

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

当您在设计时知道类型时,Generics 是有效的。 在您的情况下,您的收藏可以存储任何不同类型的项目。

您可以利用一个非常流行的技巧来声明集合项的非泛型和继承的泛型版本。 作为副作用,您可以摆脱不太可读的值元组:)

像这样声明项目 class :

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

然后您可以将它们存储为StatisticsListItem并根据它们的特定子类型执行操作。 它也是类型安全的,因为您无法访问通用列表项的值,例如如果您使用object Value { get; }将其声明为非通用StatisticsListItem object Value { get; }财产。

最简单的使用场景可以是这样的:

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

这只是一个例子。 您仍然需要以支持键、适合您的字典并使按类型过滤项目更有效的方式来实现它。 祝你好运!

一些小评论:您在处理多线程方面可能仍然存在一些问题,但这不在此问题的 scope 范围内:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM