Я поднимаю это, потому что вы можете сделать это - , пока эти переменные на самом деле являются полями / свойствами . Нужно ли вам - это другое дело - это решение, которое можно многократно использовать, медленное (требует кэширования делегатов), и я должен сказать, что я согласен с Марком Грэйвеллом - рассмотрите возможность использования массива везде, если можете.
Если переменные являются свойствами, их нужно изменить. Также, если вам нужно записать массив обратно в переменные за один раз (поскольку это решение генерирует массив с копиями всех двойников, я бы не стал создавать массив объектов с двойными числами в штучной упаковке), для которого требуется другой метод ...
Так что вот так. Сначала священная стена кода / метод расширения:
//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);
}
}
Метод расширения, описанный выше, будет работать с любым типом. Он универсален как для типа экземпляра, так и для желаемого типа массива, чтобы упростить создание лямбда-кода в коде - хотя у него нет общего значения .
Вы передаете префикс имени для группы полей - в вашем случае "weight"
- затем он ищет во всех открытых и закрытых полях экземпляра те из них с таким префиксом, которые также имеют суффикс, который можно проанализировать в целое число. Затем он упорядочивает эти поля на основе этого порядкового номера. Он не проверяет пропуски в порядковом списке - поэтому тип с weight0
и weight2
будет работать, но будет создавать только двухэлементный массив.
Затем он запекает динамический фрагмент кода с использованием деревьев выражений, компилирует его (в этот момент, как уже упоминалось в коде, было бы хорошо кэшировать делегат для TObj
и TArray
для будущего использования), а затем выполняет его, возвращая результат.
Теперь добавьте это к тестовому классу в стандартном модульном тестовом проекте:
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));
}
Как я уже сказал - я не потворствую этому, и при этом я не жду никаких +1 за это - но я отстой в вызове:)