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
vsT
的开销是多少?我知道这是一个有点通用的问题,因为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)
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);
...
}