How to initialize a ConcurrentDictionary? Error: "Cannot access private method 'Add' here"

How to initialize a ConcurrentDictionary? Error: "Cannot access private method 'Add' here"

我有一个静态 class,我在其中使用字典作为查找 table 以在 .NET 类型和 SQL 类型之间进行映射。这是一个这样的字典的例子:

private static readonly Dictionary<Type, string> SqlServerMap = new Dictionary<Type, string>
{
    {typeof (Boolean), "bit"},
    {typeof (Byte[]), "varbinary(max)"},
    {typeof (Double), "float"},
    {typeof (Byte), "tinyint"},
    {typeof (Int16), "smallint"},
    {typeof (Int32), "int"},
    {typeof (Int64), "bigint"},
    {typeof (Decimal), "decimal"},
    {typeof (Single), "real"},
    {typeof (DateTime), "datetime2(7)"},
    {typeof (TimeSpan), "time"},
    {typeof (String), "nvarchar(MAX)"},
    {typeof (Guid), "uniqueidentifier"}
};

然后我有一个 public 方法,它在下面传入一个 .NET 类型,它 returns 使用此字典的相应 MS SQL 服务器类型的字符串值。但是,由于这被用作进行数据库查询的查找 table,我认为将其设为 ConcurrentDictionary 是有意义的。我改成了:

private static readonly IDictionary<Type, string> SqlServerMap = new ConcurrentDictionary<Type, string>
{
    {typeof (Boolean), "bit"},
    {typeof (Byte[]), "varbinary(max)"},
    {typeof (Double), "float"},
    {typeof (Byte), "tinyint"},
    {typeof (Int16), "smallint"},
    {typeof (Int32), "int"},
    {typeof (Int64), "bigint"},
    {typeof (Decimal), "decimal"},
    {typeof (Single), "real"},
    {typeof (DateTime), "datetime2(7)"},
    {typeof (TimeSpan), "time"},
    {typeof (String), "nvarchar(MAX)"},
    {typeof (Guid), "uniqueidentifier"}
};

但现在它在 {}(即 ConcurrentDictionary 的所有键值对)中用红色下划线,错误是:

Cannot access private method 'Add' here

我不认为是因为我将它初始化为private static readonly,因为我刚刚通过制作public static版本进行了测试,但我得到了同样的错误。

您用来填充集合的集合初始值设定项仅在集合具有具有适当签名和可访问性的 Add 方法时才有效。 ConcurrentDictionary 没有 public Add 方法,因此您将无法对其使用集合初始值设定项。

您可以通过将 IEnumerable<KeyValuePair<TKey, TValue>> 作为参数传递给构造函数来提供一些初始数据,或者您可以调用 TryAdd(或 AddOrUpdate,或任何其他方法Add 在名称中)在创建 ConcurrentDictionary.

之后循环

正如@Servy 在他的回答中所说,集合初始化适用于具有 Add 方法的类型。但如果存在名称为 Add 和适当签名的扩展方法,它也应该有效。所以你可以为并发字典创建一个。初始化将是线程安全的,因为您使用的是静态字段初始化程序。

试试这个

private static readonly IDictionary<Type, string> SqlServerMap =
    new ConcurrentDictionary<Type, string>(
        new Dictionary<Type, string>()
        {
            {typeof(Boolean ), "bit"             },
            {typeof(Byte[]  ), "varbinary(max)"  },
            {typeof(Double  ), "float"           },
            {typeof(Byte    ), "tinyint"         },
            {typeof(Int16   ), "smallint"        },
            {typeof(Int32   ), "int"             },
            {typeof(Int64   ), "bigint"          },
            {typeof(Decimal ), "decimal"         },
            {typeof(Single  ), "real"            },
            {typeof(DateTime), "datetime2(7)"    },
            {typeof(TimeSpan), "time"            },
            {typeof(String  ), "nvarchar(MAX)"   },
            {typeof(Guid    ), "uniqueidentifier"}
        }
    );

更新:如果您使用的是 C#6(Roslyn 2.0 编译器),则可以使用新的字典初始化器。

private static readonly IDictionary<Type, string> SqlServerMap =
    new ConcurrentDictionary<Type, string>
    {
        [typeof(Boolean )] = "bit"             ,
        [typeof(Byte[]  )] = "varbinary(max)"  ,
        [typeof(Double  )] = "float"           ,
        [typeof(Byte    )] = "tinyint"         ,
        [typeof(Int16   )] = "smallint"        ,
        [typeof(Int32   )] = "int"             ,
        [typeof(Int64   )] = "bigint"          ,
        [typeof(Decimal )] = "decimal"         ,
        [typeof(Single  )] = "real"            ,
        [typeof(DateTime)] = "datetime2(7)"    ,
        [typeof(TimeSpan)] = "time"            ,
        [typeof(String  )] = "nvarchar(MAX)"   ,
        [typeof(Guid    )] = "uniqueidentifier"
    };

例子 https://dotnetfiddle.net/9ZgjsR

作为 Servy 接受的答案的代码示例,为了在实例化时初始化 ConcurrentDictionary,您可以传递一个实现 IEnumerable(如 List)的类型 KeyValuePair 构造函数的类型:

private static readonly IDictionary<Type, string> SqlServerMap =
    new ConcurrentDictionary<Type, string>(
        new List<KeyValuePair<Type, string>>
        {
            new KeyValuePair<Type, string>(typeof(Boolean), "bit"),
            new KeyValuePair<Type, string>(typeof(Boolean), "bit"),
            new KeyValuePair<Type, string>(typeof(Byte[]), "varbinary(max)"),
            new KeyValuePair<Type, string>(typeof(Double), "float"),
            new KeyValuePair<Type, string>(typeof(Byte), "tinyint"),
            new KeyValuePair<Type, string>(typeof(Int16), "smallint"),
            new KeyValuePair<Type, string>(typeof(Int32), "int"),
            new KeyValuePair<Type, string>(typeof(Int64), "bigint"),
            new KeyValuePair<Type, string>(typeof(Decimal), "decimal"),
            new KeyValuePair<Type, string>(typeof(Single), "real"),
            new KeyValuePair<Type, string>(typeof(DateTime), "datetime2(7)"),
            new KeyValuePair<Type, string>(typeof(TimeSpan), "time"),
            new KeyValuePair<Type, string>(typeof(String), "nvarchar(MAX)"),
            new KeyValuePair<Type, string>(typeof(Guid), "uniqueidentifier")
        });

由于您的 collection 不会改变,您现在可以使用 ImmutableDictionary。 虽然这在初始化方面也有问题,但针对此 question about initialization:

提出了解决方案

@LukášLánský 提供的一个简单解决方案是

var d = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } }.ToImmutableDictionary();

@IanGriffiths 提供的性能更好的版本是

public struct MyDictionaryBuilder<TKey, TValue> : IEnumerable
{
    private ImmutableDictionary<TKey, TValue>.Builder _builder;

    public MyDictionaryBuilder(int dummy)
    {
        _builder = ImmutableDictionary.CreateBuilder<TKey, TValue>();
    }

    public void Add(TKey key, TValue value) => _builder.Add(key, value);

    public TValue this[TKey key]
    {
        set { _builder[key] = value; }
    }

    public ImmutableDictionary<TKey, TValue> ToImmutable() => _builder.ToImmutable();

    public IEnumerator GetEnumerator()
    {
        // Only implementing IEnumerable because collection initializer
        // syntax is unavailable if you don't.
    throw new NotImplementedException();
    }
}