Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.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# 效率:Func<;T>;,Vs T实例_C#_.net_Generics_Functional Programming - Fatal编程技术网

C# 效率:Func<;T>;,Vs T实例

C# 效率:Func<;T>;,Vs T实例,c#,.net,generics,functional-programming,C#,.net,Generics,Functional Programming,最近我一直在尝试使用Func类,到目前为止,我非常喜欢它。然而,我注意到我越来越多地开始使用它,而不是实际使用T的实例,所以我想问一下使用FuncvsT的开销是多少?我知道这是一个有点通用的问题,因为T可以是任何东西,所以我想这个问题应该集中在,传递函数的开销是多少,而不是简单对象的实例? 为了便于论证,让我们假设以下情况 我们的模拟对象,T 现在,假设我们在IDictionary上有一个相当不错的扩展方法,它通过键选择值或默认值。伪代码可以描述如下: 是在KeyValuePair集合中找到的密

最近我一直在尝试使用
Func
类,到目前为止,我非常喜欢它。然而,我注意到我越来越多地开始使用它,而不是实际使用
T
的实例,所以我想问一下使用
Func
vs
T
的开销是多少?
我知道这是一个有点通用的问题,因为
T
可以是任何东西,所以我想这个问题应该集中在,传递函数的开销是多少,而不是简单对象的实例?

为了便于论证,让我们假设以下情况

我们的模拟对象,
T
现在,假设我们在
IDictionary
上有一个相当不错的扩展方法,它通过键选择值或默认值。伪代码可以描述如下:
是在KeyValuePair集合中找到的密钥 是,返回值 否,返回默认值

备选案文1。我们使用
T
公共静态TValue GetValueOrDefault(此IDictionary源代码,TKey键,TValue@default)
{
if(来源:ContainsKey(关键))
{
返回源[键];
}
返回@default;
}
//用法
var myValue=myDictionary.GetValueOrDefault(“Richard”,newperson());
备选案文2。我们的扩展方法使用
Func
。。。嗯,漂亮!
public static TValue GetValueOrDefault(此IDictionary源、TKey键、Func defaultSelector)
{
if(来源:ContainsKey(关键))
{
返回源[键];
}
返回defaultSelector();
}
//用法
var myValue=myDictionary.GetValueOrDefault(“Richard”,()=>新人(“Richard”,25,true));
比较
比较以上两种选择,显然两者都有潜在的好处。选项1稍微容易阅读,但我目前喜欢使用
Func
,因此对我来说,选项2似乎很理想。我想我认为它是一个延迟实例化的参数,仅在需要时执行,因此节省了效率,但我说的对吗?

这是我用于基准测试的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication3
{
    using System.Collections;
    using System.Diagnostics;
    using System.Globalization;
    using System.Numerics;
    using System.Xml.Linq;

    public class Program
    {

        public class Person
        {
            private string _name = string.Empty;

            private int _age = 0;

            private bool _isMale = true;

            public Person(string name, int age, bool isMale)
            {
                this.Name = name;
                this.Age = age;
                this.IsMale = isMale;
            }

            public string Name
            {
                get
                {
                    return this._name;
                }
                set
                {
                    this._name = value;
                }
            }

            public int Age
            {
                get
                {
                    return this._age;
                }
                set
                {
                    this._age = value;
                }
            }

            public bool IsMale
            {
                get
                {
                    return this._isMale;
                }
                set
                {
                    this._isMale = value;
                }
            }
        }

        private static void Main(string[] args)
        {
            var myDictionary = new Dictionary<string, Person>();
            myDictionary.Add("notRichard", new Program.Person("Richard1", 26, true));
            myDictionary.Add("notRichard1", new Program.Person("Richard2", 27, true));
            myDictionary.Add("notRichard2", new Program.Person("Richard3", 28, true));
            myDictionary.Add("notRichard3", new Program.Person("Richard4", 29, true));
            // usage
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", new Program.Person("Richard", 25, true));
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", ()=> new Program.Person("Richard", 25, true));
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadKey();
        }
    }
    public static class Ex
    {
        public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, TValue @default)
        {
            if (source.ContainsKey(key))
            {
                return source[key];
            }
            return @default;
        }
        public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Func<TValue> defaultSelector)
        {
            if (source.ContainsKey(key))
            {
                return source[key];
            }
            return defaultSelector();
        }


    }
}

因此,从理论上讲(如果你把实例化从等式中去掉),在调用堆栈中保存一个跃点会给你带来一些性能提升,但在实践中,这个提升可以忽略不计。

传递一个函数的引用和传递一个对象的引用,我希望这两种方式非常相似。如果您正在执行一百万个这样的调用,那么这两个调用都可能会通过保存引用并每次重用相同的值而受益。但请注意,在第一种情况下,每次都可以使默认对象相同:

Person defaultPerson = new Person();
var myValue = myDictionary.GetValueOrDefault("Richard", defaultPerson);
但在第二种情况下,每次返回默认值时,您都会实例化一个全新的
人员

Func<Person> defaultPersonFunc = () => new Person("Richard", 25, true);
var myValue = myDictionary.GetValueOrDefault("Richard", defaultPersonFunc);
Func defaultPersonFunc=()=>新人(“Richard”,25岁,真);
var myValue=myDictionary.GetValueOrDefault(“Richard”,defaultPersonFunc);
您可以通过更改为:

Person defaultPerson = new Person("Richard", 25, true);
Func<Person> defaultPersonFunc = () => defaultPerson;
var myValue = myDictionary.GetValueOrDefault("Richard", defaultPersonFunc);
persondefaultperson=新人(“Richard”,25,true);
Func defaultPersonFunc=()=>defaultPerson;
var myValue=myDictionary.GetValueOrDefault(“Richard”,defaultPersonFunc);

但是,我看不出您通过使用
Func

会获得什么好处。代理很好,但我不确定您是否能从中受益

选项1效率低下,因为您无条件创建新对象实例:

var myValue = myDictionary.GetValueOrDefault("Richard", new Person());
始终会创建一个新人

选项2效率低下,因为您创建了一个新的
Func
实例。您的语法是这方面的简写:

var myValue = myDictionary.GetValueOrDefault("Richard", new Func<Person>(()=> {
    Person("Richard", 25, true));
});
当目标不在字典中时,从另一个答案运行测试:

  • 7309毫秒(选项1)
  • 8705毫秒(选项2)
  • 5972毫秒(TryGetValue)
当目标在字典中时(例如,它永远不需要使用默认值):

  • 10026毫秒(选项2)
  • 7712毫秒(选项2)
  • 3491毫秒(TryGetValue)
很明显,无论发生什么情况,它的速度都要快得多。这是有意义的——因为无论每次对选项1或2进行测试的结果如何,都必须创建一个对象。在
Func
场景中,您必须在需要默认值时创建两个对象,在不需要默认值时创建一个对象,因此在始终需要默认值时情况最糟

另一方面,只需使用
TryGetValue
并仅有条件地执行默认代码,就只需在需要默认代码时创建一个对象(并且仅创建您真正需要的对象)

有时候,老式的方式是最好的:)

FWIW-我认为像
GetValueOrDefault
这样的方法肯定有用,但当您必须显式定义默认值时,可能就不有用了。也就是说,我看不到使用委托在代码风格方面有什么巨大的好处,当然也没有性能方面的好处。但是,如果您不需要实际定义默认person的内容,那么为什么不创建如下扩展方法:

public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, 
    TKey key) where TValue: new()
{
    TValue value;
    if (!source.TryGetValue(key, out value)) {
        value = new TValue();
    }
    return value;
}
公共静态TValue GetValuerDefault(此IDictionary源,
TKey键)其中TValue:new()
{
t价值;
如果(!source.TryGetValue(键,输出值)){
值=新的TValue();
}
返回值;
}

第二种方法效率低下,因为它要求编译器在每次调用函数时生成闭包(委托和对象实例的组合),而不管是否实际需要闭包。除非
Person
的创建成本很高,否则无条件生成闭包将比无条件生成
Person
更糟糕

另一种方法是将lambda作为静态方法,将其参数作为ref结构接受。我希望C#能够为这种方法提供一些语言支持,因为它可以更有效地完成闭包可以完成的许多事情。
Person defaultPerson = new Person("Richard", 25, true);
Func<Person> defaultPersonFunc = () => defaultPerson;
var myValue = myDictionary.GetValueOrDefault("Richard", defaultPersonFunc);
var myValue = myDictionary.GetValueOrDefault("Richard", new Person());
var myValue = myDictionary.GetValueOrDefault("Richard", new Func<Person>(()=> {
    Person("Richard", 25, true));
});
Person myValue;
if (!myDictionary.TryGetValue("Richard", out myValue)) {
    myValue = new Person("Richard",25,true);
}
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, 
    TKey key) where TValue: new()
{
    TValue value;
    if (!source.TryGetValue(key, out value)) {
        value = new TValue();
    }
    return value;
}
public delegate TResult TFuncByRef<TParam,TResult>(ref TParam); public static TValue GetValueOrDefault<TKey, TValue, TParam> (this IDictionary source, TKey key, FuncByRef<TParam, TValue> defaultSelector, ref TParam param) { ref TValue Result = default(TValue); if (!source.TryGetValue(key, ref Result)) Result = defaultSelector(ref param); return Result; } struct CreatePersonParams {public string Name; public int Age; public bool IsMale}; static Person CreatePersonByName(ref CreatePersonParams param) { return new Person(param.Name, param.Age, param.IsMale); } ... then to use it... { ... CreatePersonParams newPersonParams; newPersonParams.Name = "Emily"; newPersonParams.Age = 23; newPersonParams.IsMale = False; ... whatever = myDict.GetValueOrDefault(keyValue, CreatePersonByName, ref newPersonParams); ... }