在C#中,如何在运行时创建值类型变量? [英] In C#, how can I create a value type variable at runtime?
问题描述
我正在尝试实现类似的方法:
I am attempting to implement a method like:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
}
它将返回两个运行时生成的lambda,它们使用Expression
树来获取和设置动态创建的变量来创建代码.
It will return two runtime generated lambdas that get and set a dynamically created variable using Expression
trees to create the code.
我当前的解决方案是动态创建一个带有一个元素的类型的数组,并引用该数组:
My current solution is to dynamically create an array of the type with one element, and reference that:
(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);
}
与使用数组相比,在运行时是否有更好的方法来创建可以被读取/写入的值类型变量?
Is there a better way to create what may be a value type variable at runtime that can be read/written to than using an array?
除了使用lambda创建要在ArrayIndex
/ArrayAccess
方法调用中使用的(field?)引用以外,还有更好的方法来引用运行时创建的数组吗?
Is there a better way to reference the runtime created array other than using a lambda to create the (field?) reference for use in the ArrayIndex
/ArrayAccess
method calls?
过多的背景信息 对于那些想知道的人,最终出现这种尝试是为了为Perl哈希创建类似于Perl的左值自动虚拟化.
Excessive Background Info For those that wonder, ultimately this came up in an attempt to create something like Perl auto-virification of lvalues for Perl hashes.
假设您有一个List<T>
,其中元素重复,并且想创建一个Dictionary<T,int>
,它允许您查找列表中每个唯一T
的计数.您可以使用几行代码进行计数(在这种情况下,T
是int
):
Imagine you have a List<T>
with duplicate elements and want to create a Dictionary<T,int>
that allows you to look up the count for each unique T
in the list. You can use a few lines of code to count (int this case T
is 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:
But I want to do this with LINQ, and I want to avoid double-indexing countDict
(interestingly, ConcurrentDictionary
has AddOrUpdate
for this purpose) so I use Aggregate:
var countDict = testarray.Aggregate(new Dictionary<int,int>(), (d, n) => { ++d[n]; return d; });
但这有两个问题.首先,Dictionary
不会为缺失值创建一个值,因此您需要一种新型的Dictionary
类型,该类型可以使用例如来自动创建缺失值.种子lambda:
But this has a couple of issues. First, Dictionary
won't create a value for a missing value, so you need a new type of Dictionary
that auto-creates missing values using e.g. a seed 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#应该是有效的,但C#仍然不接受++d[n].Value;
,因此您必须创建一个临时文件.)
But you still have the lvalue problem, so you replace the plain int
counter with a Ref
class. Unfortunately, C# can't create a C++ first class Ref
class, but using one based around auto-creating a setter lambda from a getter lambda (using expression trees) is close enough. (Unfortunately C# still won't accept ++d[n].Value;
even though it should be valid, so you have to create a temporary.)
但是现在您遇到了创建多个运行时整数变量来保存计数的问题.我扩展了Ref<>
类,以使用返回一个常量(ConstantExpression
)的lambda并创建一个运行时变量,并以该常量为初始值构建一个getter和setter.
But now you have the problem of creating multiple runtime integer variables to hold the counts. I extended the Ref<>
class to take a lambda that returns a constant (ConstantExpression
) and create a runtime variable and build a getter and setter with the constant being the initial value.
推荐答案
我同意一些问题评论者的观点,即表达式树似乎是不必要的,因此这是所显示的API的简单实现,没有它们:
I agree with some of the question commenters that expression trees seem unnecessary, so here is a simple implementation of the shown API without them:
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);
}
作为对上述问题的答案(如何在没有lambda的情况下定义dynref
),对dynvar
和dynref
进行以下修改是否有问题?
As an answer to the stated question (how to define dynref
without a lambda), then, is there something wrong with the following modifications to dynvar
and dynref
?
var dynvar = new T[] { initialVal };
var dynref = Expression.Constant(dynvar);
这篇关于在C#中,如何在运行时创建值类型变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!