C#:动态构造变量

时间:2012-01-16 09:21:51

标签: c# c#-4.0

我从输入中得到一组名为weight0weight1 ... weight49的双变量。

我想动态地将它们插入到double数组中以便于操作。 但不是像每个人一样调用:Weights[0] = weight0 ... Weights[49] = weight49我想用一个循环来做。

有办法吗?

4 个答案:

答案 0 :(得分:4)

不,基本上 - 除非你的意思是你创建数组的同时:

var weights = new[] {weight0, weight1, weight2, ... , weight48, weight49};

就个人而言,我很想摆脱 50个变量,并从一开始就使用数组,但在所有情况下都可能无法实现。

答案 1 :(得分:1)

你可以使用反射来从变量名中确定数组的索引,但这远非有效。有关详细信息,请参阅此post

答案 2 :(得分:0)

我会尝试使用KeyValuePair- Listobject

    // sample data
    var weight = 1.00;

    // create a list
    var tmp = new List<KeyValuePair<string,object>>();

    // Here you can add your variables
    tmp.Add(new KeyValuePair<string,object>("weights" + tmp.Count.ToString()
            , weight));

    // If needed convert to array
    var weights = tmp.ToArray();

    // get the information out of the array
    var weightValue = weights[0].Value;
    var weightKey = weights[0].Key;

我认为这将为您提供阵列所需的所有选项。试一试。

答案 3 :(得分:0)

我这样做是因为你可以这样做 - 只要这些变量实际上是字段/属性。你应该是否是另一回事 - 这个解决方案虽然可重用,但速度很慢(需要代理缓存)而且我不得不说我同意Marc Gravell - 如果可以的话,考虑使用整个数组。

如果变量是属性,则需要更改。此外,如果你需要一次性将数组写回变量(因为这个解决方案生成一个包含所有双精度副本的数组,我不会考虑创建一个带有盒装双精度的对象数组),这需要另一种方法......

所以这里。首先是代码/扩展方法的圣墙:

//paste this as a direct child of a namespace (not a nested class)
public static class SO8877853Extensions
{
  public static TArray[] FieldsToArray<TObj, TArray>(this TObj o,string fieldPrefix)
  {
    if(string.IsNullOrWhiteSpace(fieldPrefix))
      throw new ArgumentException("fieldPrefix must not null/empty/whitespace", 
      "fieldPrefix");

    //I've done this slightly more expanded than it really needs to be...
    var fields = typeof(TObj).GetFields(System.Reflection.BindingFlags.Instance
          | System.Reflection.BindingFlags.Public
          | System.Reflection.BindingFlags.NonPublic)
     .Where(f =>f.Name.StartsWith(fieldPrefix) && f.FieldType.Equals(typeof(TArray)))
     .Select(f =>new{ Field = f, OrdinalStr = f.Name.Substring(fieldPrefix.Length)})
     .Where(f => { int unused; return int.TryParse(f.OrdinalStr, out unused);})
     .Select(f => new { Field = f.Field, Ordinal = int.Parse(f.OrdinalStr) })
     .OrderBy(f => f.Ordinal).ToArray();  
     //doesn't handle ordinal gaps e.g. 0,1,2,7

    if(fields.Length == 0)
      throw new ArgumentException(
        string.Format("No fields found with the prefix {0}", 
                      fieldPrefix), 
        "fieldPrefix");

    //could instead bake the 'o' reference as a constant - but if 
    //you are caching the delegate, it makes it non-reusable.
    ParameterExpression pThis = Expression.Parameter(o.GetType());

    //generates a dynamic new double[] { var0, var1 ... } expression
    var lambda = Expression.Lambda<Func<TObj, TArray[]>>(
      Expression.NewArrayInit(typeof(TArray), 
      fields.Select(f => Expression.Field(pThis, f.Field))), pThis);
    //you could cache this delegate here by typeof(TObj), 
    //fieldPrefix and typeof(TArray) in a Dictionary/ConcurrentDictionary
    return lambda.Compile()(o);
  }
}

上面的扩展方法适用于任何类型。它在实例类型和所需的数组类型上都是通用的,以简化代码中lambda的创建 - 但 并不是通用的。

您传入一组字段的名称前缀 - 在您的情况下为"weight" - 然后它会搜索所有公共和私有实例字段,以查找具有该前缀且具有后缀的那些字段,该后缀可以解析为整数。然后它根据该序数对这些字段进行排序。它不检查序数列表中的间隙 - 因此weight0weight2的类型可以工作,但只能创建一个双元素数组。

然后它使用表达式树烘焙一段动态代码,编译它(此时,如代码中所述,最好将委托缓存到TObjTArray以备将来使用使用)然后执行它,返回结果。

现在将其添加到标准单元测试项目中的测试类中:

private class SO8877853
{
    private double field0 = 1.0;
    private double field1 = -5.0;
    private double field2 = 10.0;
    public double[] AsArray()
    {
        //it would be nice not to have to pass both type names here - that 
        //can be achieved by making the extension method pass out the array
        //via an 'out TArray[]' instead.
        return this.FieldsToArray<SO8877853, double>("field");
    }
}

[TestMethod]
public void TestThatItWorks()
{
    var asArray = new SO8877853().AsArray();
    Assert.IsTrue(new[] { 1.0, -5.0, 10.0 }.SequenceEqual(asArray));
}

就像我说的那样 - 我不是在纵容这样做,我也不期待任何+ 1s - 但我是一个挑战的傻瓜:)