簡體   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