我遇到了以下问题。
考虑以下简单属性。
[AttributeUsage(AttributeTargets.Property)]
public class CombinationsAttribute : Attribute
{
public object[] PossibleValues { get; private set; }
public CombinationsAttribute(params object[] values)
{
this.PossibleValues = values;
}
}
以下是属性用法示例 - 只是一些具有一些虚拟属性的类,进入属性的值数组始终是属性类型。
public class MyClass
{
[Combinations(1, 2, 3, 4, 5)]
public int IntProperty1 { get; set; }
[Combinations(10, 15, 20, 25, 30)]
public int IntProperty2 { get; set; }
[Combinations("X", "Y", "Z")]
public string StringProperty { get; set; }
}
我希望获得所有组合的所有实例(在此示例中为5 * 5 * 3)。如何尽可能少编写代码(LINQ青睐)?
编辑:我不知道类(MyClass) - 有许多具有CombinationsAttribute的公共属性的类,我需要为它们计算所有可能的组合。 这些类总是有无参数构造函数。
预期结果示例(用于可视化的伪c#):
List<MyClass> Combinations = GetCombinationMagicFunction(typeof(MyClass));
List[0] = MyClass { IntProperty1 = 1, IntProperty2 = 10, StringProperty = "X" }
List[1] = MyClass { IntProperty1 = 1, IntProperty2 = 10, StringProperty = "Y" }
List[2] = MyClass { IntProperty1 = 1, IntProperty2 = 10, StringProperty = "Z" }
List[2] = MyClass { IntProperty1 = 1, IntProperty2 = 15, StringProperty = "X" }
...
List[74] = MyClass { IntProperty1 = 5, IntProperty2 = 30, StringProperty = "Z" }
答案 0 :(得分:1)
在Eric Lippert的CartesianProduct
method:
private static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(emptyProduct, (accumulator, sequence) => accumulator.SelectMany(accseq => sequence.Select(item => accseq.Concat(new[] { item }))));
}
public static IEnumerable<T> BuildCombinations<T>() where T : new()
{
var query = from prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
let attributes = prop.GetCustomAttributes(typeof(CombinationsAttribute), false)
where attributes != null && attributes.Length != 0
let attribute = (CombinationsAttribute)attributes[0]
select attribute.PossibleValues.Select(value => new { prop, value })
;
var combinations = CartesianProduct(query);
foreach (var combination in combinations)
{
var item = new T();
foreach (var pair in combination)
{
pair.prop.SetValue(item, pair.value, null);
}
yield return item;
}
}
用法:
var list = BuildCombinations<MyClass>().ToList();
答案 1 :(得分:0)
我的基本出发点是沿着这些方向。你需要使用一些反射来设置值,但你应该明白这一点。
var properties = type.GetProperties()
.Where(prop => prop.IsDefined(typeof(CombinationsAttribute), false));
foreach(var prop in properties)
{
allCombinations.Add(instance);
var attributes = (CombinationsAttribute[])prop.GetCustomAttributes(typeof(CombinationsAttribute), false);
foreach(var value in attributes)
{
}
}
答案 2 :(得分:0)
private class PropertyItem
{
public PropertyInfo Property { get; set; }
public List<object> PossibleValues { get; set; }
public int CurrentIndex { get; set; }
public int Count { get; set; }
}
public IEnumerable<T> GenerateCombinations<T>() where T : new()
{
return GenerateCombinations(typeof(T)).Cast<T>();
}
public IEnumerable<object> GenerateCombinations(Type type)
{
// Collect nessecery information
var constructor = type.GetConstructor(Type.EmptyTypes);
var properties =
(
from p in type.GetProperties()
let values = p.GetCustomAttributes(typeof(CombinationsAttribute), true)
.Cast<CombinationsAttribute>()
.SelectMany(a => a.PossibleValues)
.ToList()
where values.Count > 0
select new PropertyItem
{
Property = p,
PossibleValues = values,
CurrentIndex = 0,
Count = values.Count,
}
)
.ToList();
bool isDone;
do
{
// Construct and return the current item
var item = constructor.Invoke(new object[0]);
foreach (var prop in properties)
{
prop.Property.SetValue(item, prop.PossibleValues[prop.CurrentIndex]);
}
yield return item;
// Move the indices to the next item
isDone = true;
for (int i = 0; i < properties.Count; i++)
{
var prop = properties[i];
if (prop.CurrentIndex == prop.Count - 1)
{
prop.CurrentIndex = 0;
}
else
{
prop.CurrentIndex++;;
isDone = false;
break;
}
}
}
while (!isDone);
}
<强>用法:强>
var allObjects1 = GenerateCombinations<MyClass>().ToList();
var allObjects2 = GenerateCombinations(typeof(MyClass)).ToList();