Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从性能角度看ExpandoObject与Dictionary?_C#_Dictionary_Expandoobject - Fatal编程技术网

C# 从性能角度看ExpandoObject与Dictionary?

C# 从性能角度看ExpandoObject与Dictionary?,c#,dictionary,expandoobject,C#,Dictionary,Expandoobject,一个相当简单的问题。我正在做一个项目,需要从一种上下文存储中动态存储和检索属性值。这些值将不时写入并读取多次。检索速度是这里的首要任务,每纳秒都很重要 通常,我只是用一个字典来实现这一点,但是用C#4和ExpandoObject,我想也许有更好的方法?有人有这方面的经验吗?我在其他帖子中看到,它不是用字典实现的,这让我好奇它是快还是慢 让我试着用一些伪代码来澄清: // In the main loop var context = new Context(); context["MyKey"]

一个相当简单的问题。我正在做一个项目,需要从一种上下文存储中动态存储和检索属性值。这些值将不时写入并读取多次。检索速度是这里的首要任务,每纳秒都很重要

通常,我只是用一个字典来实现这一点,但是用C#4和ExpandoObject,我想也许有更好的方法?有人有这方面的经验吗?我在其他帖子中看到,它不是用字典实现的,这让我好奇它是快还是慢

让我试着用一些伪代码来澄清:

// In the main loop
var context = new Context();
context["MyKey"] = 123;
context["MyOtherKey"] = "CODE";
context["MyList"] = new List<int>() { 1, 12, 14 };

foreach(var handler in handlers) {
    handler.DoStuff(context);
}
好吧,希望你明白我的意思

我也完全接受这里的其他建议。我一直在考虑让上下文类静态类型化的想法(例如,实际上有一个
MyKey
属性,一个
MyOtherKey
属性等等),虽然这可能会极大地阻碍我们的生产力

检索速度是这里的首要任务,每纳秒都很重要

dynamic
相关的任何东西都可能不是您想要的

不要误解我的意思,它经过了大量的优化,但是如果你基本上只需要一个字符串到字符串的字典查找,请坚持使用字典


或者,如果您的键数量有限,您是否考虑过只使用一个带有枚举或一组
int
常量的数组作为键?

在第一次调用时,它是否必须那么快?由于调用站点缓存,为动态对象(包括添加到动态对象中的方法)创建的表达式树在编译后被缓存,并在再次使用时返回


使用ExpandoObject应该可以,但是如果您真的需要获得绝对最佳的性能,也许您应该使用自定义类型。

如果事先知道字符串列表,您可以使用IL Emit根据搜索字符串中的字符创建分支树,并将索引解析为数组。这将使您的查找速度非常快

我在学习IL Emit的时候,为了好玩和练习而实现了类似的东西。它基于我尝试过的有限测试用例工作,但您肯定希望使它更健壮,并为生产代码创建适当的单元测试。我已经发布了原始代码(有点长);你需要为你的特殊情况改变一些事情,但核心逻辑是存在的。我没有包括
EmitLdc
helper函数(有很多重载),但它只是一个将任意常量加载到堆栈的函数。您可以简单地分别使用Ldstr和Ldc_I4替换调用以直接发出字符串和数字类型

    protected void GenerateNestedStringSearch<T>(ILGenerator gen, T[] values, Func<T, string> getName, Action<ILGenerator, T> loadValue)
    {
        //We'll jump here if no match found
        Label notFound = gen.DefineLabel();

        //Try to match the string
        GenerateNestedStringSearch(gen, notFound, values, getName, loadValue, 0);

        //Nothing found, so don't need string anymore
        gen.MarkLabel(notFound);
        gen.Emit(OpCodes.Pop);

        //Throw ArgumentOutOfRangeException to indicate not found
        gen.EmitLdc("name");
        gen.EmitLdc("Binding does not contain a tag with the specified name: ");
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Call, typeof(String).GetMethod("Concat",
                                                        BindingFlags.Static | BindingFlags.Public,
                                                        null,
                                                        new[] { typeof(string), typeof(string) },
                                                        null));
        gen.Emit(OpCodes.Newobj,
                 typeof(ArgumentOutOfRangeException).GetConstructor(new[] { typeof(string), typeof(string) }));
        gen.Emit(OpCodes.Throw);
    }

    protected void GenerateNestedStringSearch<T>(ILGenerator gen, Label notFound, T[] values, Func<T, string> getName, Action<ILGenerator, T> loadValue, int charIndex)
    {
        //Load the character from the candidate string for comparison
        gen.Emit(OpCodes.Dup);
        gen.EmitLdc(charIndex);
        gen.Emit(OpCodes.Ldelem_U2);

        //Group possible strings by their character at this index
        //We ignore strings that are too short
        var strings = values.Select(getName).ToArray();
        var stringsByChar =
            from x in strings
            where charIndex < x.Length
            group x by x[charIndex]
                into g
                select new { FirstChar = g.Key, Strings = g };

        foreach (var grouped in stringsByChar)
        {
            //Compare source character to group character and jump ahead if it doesn't match
            Label charNotMatch = gen.DefineLabel();
            gen.Emit(OpCodes.Dup);
            gen.EmitLdc(grouped.FirstChar);
            gen.Emit(OpCodes.Bne_Un, charNotMatch);

            //If there is only one string in this group, we've found our match
            int count = grouped.Strings.Count();
            Debug.Assert(count > 0);
            if (count == 1)
            {
                //Don't need the source character or string anymore
                gen.Emit(OpCodes.Pop);
                gen.Emit(OpCodes.Pop);

                //Return the value for this name
                int index = Array.FindIndex(strings, s => s == grouped.Strings.First());
                loadValue(gen, values[index]);
                gen.Emit(OpCodes.Ret);
            }
            else
            {
                //Don't need character anymore
                gen.Emit(OpCodes.Pop);

                //If there is a string that ends at this character
                string endString = grouped.Strings.FirstOrDefault(s => s.Length == (charIndex + 1));
                if (endString != null)
                {
                    //Get string length
                    gen.Emit(OpCodes.Dup);
                    gen.Emit(OpCodes.Call, typeof(char[]).GetProperty("Length").GetGetMethod());

                    //If string length matches ending string
                    gen.EmitLdc(endString.Length);
                    Label keepSearching = gen.DefineLabel();
                    gen.Emit(OpCodes.Bne_Un, keepSearching);

                    //Don't need the source string anymore
                    gen.Emit(OpCodes.Pop);

                    //Create an UnboundTag for this index
                    int index = Array.FindIndex(strings, s => s == endString);
                    loadValue(gen, values[index]);
                    gen.Emit(OpCodes.Ret);

                    //String length didn't match
                    gen.MarkLabel(keepSearching);
                }

                //Need to consider strings starting with next character
                var nextValues = from s in grouped.Strings
                                 join v in values on s equals getName(v) 
                                 select v;

                GenerateNestedStringSearch(gen, notFound, nextValues.ToArray(),
                    getName, loadValue, charIndex + 1);
            }

            //This character didn't match, so consider next character
            gen.MarkLabel(charNotMatch);
        }

        //We don't need the character anymore
        gen.Emit(OpCodes.Pop);

        //No string match, so jump to Not Found at end of check
        gen.Emit(OpCodes.Br, notFound);
    }
受保护的void GenerateNestedStringSearch(ILGenerator、T[]值、Func getName、Action loadValue)
{
//如果找不到匹配的,我们就跳到这里
Label notFound=gen.DefineLabel();
//尝试匹配字符串
GenerateNestedStringSearch(gen、notFound、value、getName、loadValue、0);
//找不到任何内容,因此不再需要字符串
gen.MarkLabel(未找到);
gen.Emit(操作码Pop);
//抛出ArgumentOutOfRangeException以指示找不到
埃米特LDC(以下简称“名称”);
gen.EmitLdc(“绑定不包含具有指定名称的标记:”);
gen.Emit(操作码Ldarg_0);
gen.Emit(opcode.Call,typeof(String).GetMethod(“Concat”,
BindingFlags.Static | BindingFlags.Public,
无效的
新[]{typeof(string),typeof(string)},
空);
gen.Emit(操作码.Newobj,
GetConstructor(新[]{typeof(string),typeof(string)});
gen.Emit(操作码抛出);
}
受保护的void GenerateNestedStringSearch(ILGenerator、Label notFound、T[]值、Func getName、Action loadValue、int charIndex)
{
//从候选字符串加载字符以进行比较
gen.Emit(操作码Dup);
埃米特尔德奇将军(查林德克斯);
gen.Emit(操作码Ldelem_U2);
//按此索引处的字符对可能的字符串进行分组
//我们忽略太短的字符串
var strings=values.Select(getName.ToArray();
var stringsByChar=
从x到字符串
其中charIndex0);
如果(计数=1)
{
//不再需要源字符或字符串
gen.Emit(操作码Pop);
gen.Emit(操作码Pop);
//返回此名称的值
int index=Array.FindIndex(strings,s=>s==grouped.strings.First());
负荷值(发电机、值[索引]);
gen.Emit(操作码Ret);
}
其他的
{
//不再需要性格了
gen.Emit(操作码Pop);
//如果存在以该字符结尾的字符串
string endString=grouped.Strings.FirstOrDefault(s=>s.Length==
    protected void GenerateNestedStringSearch<T>(ILGenerator gen, T[] values, Func<T, string> getName, Action<ILGenerator, T> loadValue)
    {
        //We'll jump here if no match found
        Label notFound = gen.DefineLabel();

        //Try to match the string
        GenerateNestedStringSearch(gen, notFound, values, getName, loadValue, 0);

        //Nothing found, so don't need string anymore
        gen.MarkLabel(notFound);
        gen.Emit(OpCodes.Pop);

        //Throw ArgumentOutOfRangeException to indicate not found
        gen.EmitLdc("name");
        gen.EmitLdc("Binding does not contain a tag with the specified name: ");
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Call, typeof(String).GetMethod("Concat",
                                                        BindingFlags.Static | BindingFlags.Public,
                                                        null,
                                                        new[] { typeof(string), typeof(string) },
                                                        null));
        gen.Emit(OpCodes.Newobj,
                 typeof(ArgumentOutOfRangeException).GetConstructor(new[] { typeof(string), typeof(string) }));
        gen.Emit(OpCodes.Throw);
    }

    protected void GenerateNestedStringSearch<T>(ILGenerator gen, Label notFound, T[] values, Func<T, string> getName, Action<ILGenerator, T> loadValue, int charIndex)
    {
        //Load the character from the candidate string for comparison
        gen.Emit(OpCodes.Dup);
        gen.EmitLdc(charIndex);
        gen.Emit(OpCodes.Ldelem_U2);

        //Group possible strings by their character at this index
        //We ignore strings that are too short
        var strings = values.Select(getName).ToArray();
        var stringsByChar =
            from x in strings
            where charIndex < x.Length
            group x by x[charIndex]
                into g
                select new { FirstChar = g.Key, Strings = g };

        foreach (var grouped in stringsByChar)
        {
            //Compare source character to group character and jump ahead if it doesn't match
            Label charNotMatch = gen.DefineLabel();
            gen.Emit(OpCodes.Dup);
            gen.EmitLdc(grouped.FirstChar);
            gen.Emit(OpCodes.Bne_Un, charNotMatch);

            //If there is only one string in this group, we've found our match
            int count = grouped.Strings.Count();
            Debug.Assert(count > 0);
            if (count == 1)
            {
                //Don't need the source character or string anymore
                gen.Emit(OpCodes.Pop);
                gen.Emit(OpCodes.Pop);

                //Return the value for this name
                int index = Array.FindIndex(strings, s => s == grouped.Strings.First());
                loadValue(gen, values[index]);
                gen.Emit(OpCodes.Ret);
            }
            else
            {
                //Don't need character anymore
                gen.Emit(OpCodes.Pop);

                //If there is a string that ends at this character
                string endString = grouped.Strings.FirstOrDefault(s => s.Length == (charIndex + 1));
                if (endString != null)
                {
                    //Get string length
                    gen.Emit(OpCodes.Dup);
                    gen.Emit(OpCodes.Call, typeof(char[]).GetProperty("Length").GetGetMethod());

                    //If string length matches ending string
                    gen.EmitLdc(endString.Length);
                    Label keepSearching = gen.DefineLabel();
                    gen.Emit(OpCodes.Bne_Un, keepSearching);

                    //Don't need the source string anymore
                    gen.Emit(OpCodes.Pop);

                    //Create an UnboundTag for this index
                    int index = Array.FindIndex(strings, s => s == endString);
                    loadValue(gen, values[index]);
                    gen.Emit(OpCodes.Ret);

                    //String length didn't match
                    gen.MarkLabel(keepSearching);
                }

                //Need to consider strings starting with next character
                var nextValues = from s in grouped.Strings
                                 join v in values on s equals getName(v) 
                                 select v;

                GenerateNestedStringSearch(gen, notFound, nextValues.ToArray(),
                    getName, loadValue, charIndex + 1);
            }

            //This character didn't match, so consider next character
            gen.MarkLabel(charNotMatch);
        }

        //We don't need the character anymore
        gen.Emit(OpCodes.Pop);

        //No string match, so jump to Not Found at end of check
        gen.Emit(OpCodes.Br, notFound);
    }