我正在尝试实现一种方法:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
}
它将返回两个运行时生成的lambda,它们使用Expression
树来获取和设置动态创建的变量来创建代码。
我当前的解决方案是动态创建一个带有一个元素的类型的数组,并引用它:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var dynvar = Array.CreateInstance(typeof(T), 1);
Expression<Func<Array>> f = () => dynvar;
var dynref = Expression.Convert(f.Body, typeof(T).MakeArrayType());
var e0 = Expression.Constant(0);
var getBody = Expression.ArrayIndex(dynref, e0);
var setParam = Expression.Parameter(typeof(T));
var setBody = Expression.Assign(Expression.ArrayAccess(dynref, e0), setParam);
var getFn = Expression.Lambda<Func<T>>(getBody).Compile();
var setFn = Expression.Lambda<Action<T>>(setBody, setParam).Compile();
return (getFn, setFn);
}
与使用数组相比,在运行时是否有更好的方法来创建可以被读取/写入的值类型变量?
除了使用lambda来创建要在ArrayIndex
/ ArrayAccess
方法调用中使用的(field?)引用之外,还有一种更好的方法来引用运行时创建的数组吗?
过多的背景信息 对于那些想知道的人,最终是想创建类似Perl哈希值的Perl自动虚拟化的东西。
想象一下,您有一个包含重复元素的List<T>
,并且想要创建一个Dictionary<T,int>
,该列表允许您查找列表中每个唯一T
的计数。您可以使用几行代码进行计数(在这种情况下,T
是int
):
var countDict = new Dictionary<int, int>();
foreach (var n in testarray) {
countDict.TryGetValue(n, out int c);
countDict[n] = c + 1;
}
但是我想用LINQ做到这一点,并且我想避免对countDict
进行双索引(有趣的是,ConcurrentDictionary
为此有AddOrUpdate
),所以我使用了Aggregate:>
var countDict = testarray.Aggregate(new Dictionary<int,int>(), (d, n) => { ++d[n]; return d; });
但这有两个问题。首先,Dictionary
不会为缺少的值创建一个值,因此您需要一种新型的Dictionary
,该类型可以使用例如种子lambda:
var countDict = testarray.Aggregate(new SeedDictionary<int, Ref<int>>(() => Ref.Of(() => 0)), (d, n) => { var r = d[n]; ++r.Value; return d; });
但是您仍然遇到左值问题,因此您用int
类替换了普通的Ref
计数器。不幸的是,C#无法创建C ++第一类Ref
类,但是使用基于从getter lambda(使用表达式树)自动创建setter lambda(基于表达式树)的类已经足够接近。 (不幸的是,尽管C#应该是有效的,但它仍然不会接受++d[n].Value;
,因此您必须创建一个临时目录。)
但是现在您遇到了创建多个运行时整数变量来保存计数的问题。我扩展了Ref<>
类,以使用返回一个常量(ConstantExpression
)的lambda并创建一个运行时变量,并以该常量为初始值来构建一个getter和setter。
答案 0 :(得分:1)
我同意一些问题评论者的观点,即表达式树似乎是不必要的,因此这是所显示的API的简单实现,而没有它们:
var slider1 = noUiSlider.create($("#slider1")[0], {
start: 0,
behaviour: 'tap',
connect: 'lower',
step: 10,
tooltips: false,
range: {
'min': 0,
'max': 100
}
});
var slider2 = noUiSlider.create($("#slider2")[0], {
start: 0,
behaviour: 'tap',
connect: 'lower',
step: 10,
tooltips: false,
range: {
'min': 0,
'max': 100
}
});
作为对上述问题(如何在不使用lambda的情况下定义struct Box<T> {
public T Value;
}
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var box = new Box<T> { Value = initialVal };
return (() => box.Value, v => box.Value = v);
}
的答案)中,对dynref
和dynvar
进行以下修改是否有问题?
dynref