0

我遇到了以下问题。

考虑以下简单属性。

[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" }
4

3 回答 3

1

借助 Eric Lippert 的CartesianProduct方法

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();
于 2012-11-27T17:01:45.027 回答
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)
    {
    }
}
于 2012-11-27T16:35:44.660 回答
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();
于 2012-11-27T16:51:45.383 回答