在C#中,如何在运行时创建值类型变量? [英] In C#, how can I create a value type variable at runtime?

查看:287
本文介绍了在C#中,如何在运行时创建值类型变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现类似的方法:

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的计数.您可以使用几行代码进行计数(在这种情况下,Tint):

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),对dynvardynref进行以下修改是否有问题?

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆