3

我有一个双打列表List<double[]>,我想将其转换为List<T>其中 T 是一个类。

double 数组包含 17 个值,例如 [1.0, 2.0, 3.0, 4.0, 5.0,.. 17.0 ]。然后我有一个类,它定义了 17 个字符串属性,例如 P1、P2、....、P17

所以 an 的每个元素List<double[]>都是一个双精度数组,并且数组中的每个元素都代表 type 类中属性的一个值T

是否可以将给定双精度数组的每个索引映射到 T 类型的属性。所以我将 a 转换List<double[]>List<T>where Tis class

我知道这可以通过手动迭代列表来完成,读取每个数组,然后从数组的每个索引中读取值并将其传递给类的相应属性。但是当我有很多具有 10 多个属性的类时,它有很多事情要做。

编辑:下面给出了其中一个类的示例

    /// <summary>
/// Defines the properties of a centroid.
/// </summary>
public class Centroid
{
    // ReSharper disable InconsistentNaming
    /// <summary>
    /// Calls made to contacts
    /// </summary>
    public string CONTACT_CALLS { get; set; }

    /// <summary>
    /// Duration of calls made to contacts
    /// </summary>
    public string CONTACT_CALLS_SEC { get; set; }

    /// <summary>
    /// Calls made to non contacts
    /// </summary>      
    public string UNKNOWN_CALLS { get; set; }

    /// <summary>
    /// Duration of calls made to non contacts
    /// </summary>
    public string UNKNOWN_CALLS_SEC { get; set; }

    /// <summary>
    /// Number of SMS sent to contacts
    /// </summary>
    public string SMS_OUT_CONTACTS { get; set; }

    /// <summary>
    /// Number of SMS sent to non contacts
    /// </summary>
    public string SMS_OUT_UNKNOWN { get; set; }

    /// <summary>
    /// Percentage of CPU usaed
    /// </summary>
    public string CPU_USAGE { get; set; }

    /// <summary>
    /// Percentage of RAM used
    /// </summary>
    public string RAM_USAGE { get; set; }

    /// <summary>
    /// Number of system application
    /// </summary>
    public string SYS_APPS { get; set; }

    /// <summary>
    /// Number of user applications
    /// </summary>
    public string USER_APPS { get; set; }

    /// <summary>
    /// Number of system services
    /// </summary>
    public string SYS_SERVICES { get; set; }

    /// <summary>
    /// Number of user services
    /// </summary>
    public string USER_SERVICES { get; set; }

    /// <summary>
    /// Number of bytes sent
    /// </summary>
    public string BYTES_TX { get; set; }

    /// <summary>
    /// Number of bytes received
    /// </summary>
    public string BYTES_RX { get; set; }

    /// <summary>
    /// Latitute of the location
    /// </summary>
    public string LOC_LAT { get; set; }

    /// <summary>
    /// Longitude of the location
    /// </summary>
    public string LOC_LON { get; set; }

    /// <summary>
    /// The Time Slice
    /// </summary>
    public string TIME_SLICE { get; set; }

    // ReSharper restore InconsistentNaming
}

EDIT2: 从列表创建列表的方法,注意_profiler.FinalCentroid 是类型List<double[]>

    private void CreateListOfCentroids()
    {
        _centroidsList = new CentroidsList {Centroids = new List<Centroid>()};

        foreach (var centroid in _profiler.FinalCentroid.Select(doublese => new Centroid
        {
            CONTACT_CALLS = doublese[0].ToString(CultureInfo.InvariantCulture),
            CONTACT_CALLS_SEC = doublese[1].ToString(CultureInfo.InvariantCulture),
            UNKNOWN_CALLS = doublese[2].ToString(CultureInfo.InvariantCulture),
            UNKNOWN_CALLS_SEC = doublese[3].ToString(CultureInfo.InvariantCulture),
            SMS_OUT_CONTACTS = doublese[4].ToString(CultureInfo.InvariantCulture),
            SMS_OUT_UNKNOWN = doublese[5].ToString(CultureInfo.InvariantCulture),
            CPU_USAGE = doublese[6].ToString(CultureInfo.InvariantCulture),
            RAM_USAGE = doublese[7].ToString(CultureInfo.InvariantCulture),
            SYS_APPS = doublese[8].ToString(CultureInfo.InvariantCulture),
            USER_APPS = doublese[9].ToString(CultureInfo.InvariantCulture),
            SYS_SERVICES = doublese[10].ToString(CultureInfo.InvariantCulture),
            USER_SERVICES = doublese[11].ToString(CultureInfo.InvariantCulture),
            BYTES_TX = doublese[12].ToString(CultureInfo.InvariantCulture),
            BYTES_RX = doublese[13].ToString(CultureInfo.InvariantCulture),
            LOC_LAT = doublese[14].ToString(CultureInfo.InvariantCulture),
            LOC_LON = doublese[15].ToString(CultureInfo.InvariantCulture),
            TIME_SLICE = doublese[16].ToString(CultureInfo.InvariantCulture)
        }))
        {
            _centroidsList.Centroids.Add(centroid);
        }
    }//end method
4

4 回答 4

5

显而易见,为什么不在构造函数中分配属性呢?
您将开始创建属性的工作。
在构造函数中分配值的击键次数少于属性。

List<double[]> ld = new List<double[]>();
List<PropDouble> lpd = new List<PropDouble>();
foreach (double[] da in ld) { lpd.Add(new PropDouble(da)); }

public class PropDouble
{
    public double P0 { get; set; }
    public double P1 { get; set; }
    public PropDouble(double[] doubles) { P0 = doubles[0]; P1 = doubles[1]; }
}

或者

public class PropDouble
{
    private double[] doubles;
    public double P0 { get { return doubles[0]; } set { doubles[0] = value; } }
    public double P1 { get { return doubles[1]; } set { doubles[1] = value; } }
    public PropDouble(double[] Doubles) { doubles = Doubles; }
}
于 2013-05-13T17:48:00.643 回答
2

编辑:因为正如 Blam 所指出的,最明显的答案是一个简单的构造函数,我将针对更复杂的场景更新这个答案。

假设您从不同位置获取阵列。它们的顺序可能不同,或者可能存在缺失值。在这种情况下,您可以使用自定义属性反射将您的属性映射到不同的数组索引。每个映射都被命名,以便您可以为不同的数组使用不同的索引(或根本不使用)。

请注意,反射将大大降低成本性能。对于少数对象,此成本几乎不会引起注意,但如果您要处理数千或更多对象,那么您可能需要考虑重构。

这是用于将属性映射到索引的自定义属性

[AttributeUsage( AttributeTargets.Property, AllowMultiple = true )]
class ArrayToPropertyMap : Attribute
{
    public string ArrayName
    {
        get;
        set;
    }

    public int ArrayIndex
    {
        get;
        set;
    }

    /* Call this function to do the actuall mapping (target is updated) */
    public static IEnumerable<U> Map<U, T>( IEnumerable<T[]> source, string arrayName ) where U : new()
    {
        if ( !source.Any() )
            return new U[] { };

        var l_mappedProperties =
            typeof( U )
            .GetProperties( System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance )
            .Where(
                p => ( p.PropertyType == typeof( T ) ) && Attribute.IsDefined( p, typeof( ArrayToPropertyMap ) ) )
            .Select(
                p => new
                {
                    Property = p,
                    Attribute = p.GetCustomAttributes( typeof( ArrayToPropertyMap ), true )
                                 .Cast<ArrayToPropertyMap>()
                                 .Where( a => a.ArrayName == arrayName )
                                 .FirstOrDefault()
                } )
            .Where(
                p => p.Attribute != null )
            .Select(
                p => new
                {
                    Property = p.Property,
                    Index = p.Attribute.ArrayIndex
                } );

        var l_result = new List<U>();

        foreach ( var array in source )
        {
            var l_target = new U();
            foreach ( var l_mappedProperty in l_mappedProperties )
                l_mappedProperty.Property.SetValue( l_target, array[l_mappedProperty.Index], null );
            l_result.Add( l_target );
        }

        return l_result;
    }

}

这是一个示例类(所以你可以看到它工作)

class LotsaProps1
{

    [ArrayToPropertyMap( ArrayName = "Array1", ArrayIndex = 0 )]
    [ArrayToPropertyMap( ArrayName = "Array2", ArrayIndex = 3 )]
    public string Prop1
    {
        get;
        set;
    }

    [ArrayToPropertyMap( ArrayName = "Array1", ArrayIndex = 2 )]
    [ArrayToPropertyMap( ArrayName = "Array2", ArrayIndex = 2 )]
    public string Prop2
    {
        get;
        set;
    }

    /* Notice that Prop3 is not mapped to Array1 */
    [ArrayToPropertyMap( ArrayName = "Array2", ArrayIndex = 1 )]
    public string Prop3
    {
        get;
        set;
    }

    [ArrayToPropertyMap( ArrayName = "Array1", ArrayIndex = 1 )]
    [ArrayToPropertyMap( ArrayName = "Array2", ArrayIndex = 0 )]
    public string Prop4
    {
        get;
        set;
    }
}

这部分只是运行示例

class Program
{
    static void Main( string[] args )
    {

        /* You should already have the arrays... */
        string[][] arr1 = new string[][] { 
            new string[] { "Value A 1", "Value A 2", "Value A 3" },
            new string[] { "Value A 4", "Value A 5", "Value A 6" },
            new string[] { "Value A 7", "Value A 8", "Value A 9" },
        };
        string[][] arr2 = new string[][] { 
            new string[] { "Value B 1", "Value B 2", "Value B 3", "Value B 4" },
            new string[] { "Value B 5", "Value B 6", "Value B 7", "Value B 8" },
            new string[] { "Value B 9", "Value B 10", "Value B 11", "Value B 12" },
        };

        /* ...so this is really the only code you'd need to add to your
              business logic: */
        var l_objs1 = ArrayToPropertyMap.Map<LotsaProps1, string>( arr1, "Array1" );
        var l_objs2 = ArrayToPropertyMap.Map<LotsaProps1, string>( arr2, "Array2" );

        /* This code is just used to show that the example works: */
        Console.WriteLine( "Array1:" );
        foreach ( var l_obj in l_objs1 )
        {
            Console.Write( "Prop1='" + l_obj.Prop1 + "'; " );
            Console.Write( "Prop2='" + l_obj.Prop2 + "'; " );
            Console.Write( "Prop3='" + l_obj.Prop3 + "'; " );
            Console.WriteLine( "Prop4 = '" + l_obj.Prop4 + "'" );
        }
        Console.WriteLine( "Array2:" );
        foreach ( var l_obj in l_objs2 )
        {
            Console.Write( "Prop1='" + l_obj.Prop1 + "'; " );
            Console.Write( "Prop2='" + l_obj.Prop2 + "'; " );
            Console.Write( "Prop3='" + l_obj.Prop3 + "'; " );
            Console.WriteLine( "Prop4 = '" + l_obj.Prop4 + "'" );
        }

        Console.ReadKey( true );

    }
}

输出

Array1:
Prop1='Value A 1'; Prop2='Value A 3'; Prop3=''; Prop4 = 'Value A 2'
Prop1='Value A 4'; Prop2='Value A 6'; Prop3=''; Prop4 = 'Value A 5'
Prop1='Value A 7'; Prop2='Value A 9'; Prop3=''; Prop4 = 'Value A 8'
Array2:
Prop1='Value B 4'; Prop2='Value B 3'; Prop3='Value B 2'; Prop4 = 'Value B 1'
Prop1='Value B 8'; Prop2='Value B 7'; Prop3='Value B 6'; Prop4 = 'Value B 5'
Prop1='Value B 12'; Prop2='Value B 11'; Prop3='Value B 10'; Prop4 = 'Value B 9'
于 2013-05-13T17:49:12.623 回答
0

这是一个没有错误检查的简单实现,但您可以指定自己的转换函数。目前它默认使用 Convert.ChangeType :

void Main()
{
    var list = new List<double> { 1.0, 1.1, 2.2, 3.3 };
    var instances = new List<A> { new A(), new A(), new A(), new A() };
    ConvertZipMap(list, instances, item => item.P);

    // 1
    // 1
    // 2
    // 3
}

class A {
    public int P { get; set; }
}

void ConvertZipMap<TSource,TTarget,TProperty>(
    IEnumerable<TSource> source,
    IEnumerable<TTarget> target,
    Expression<Func<TTarget, TProperty>> propertySelector,
    Func<TSource, TProperty> conversion = null) {

    // create setter
    var member = (MemberExpression)propertySelector.Body;
    var param = Expression.Parameter(typeof(TProperty), "value");
    var setExpr = Expression.Lambda<Action<TTarget, TProperty>>(
        Expression.Assign(member, param),
        propertySelector.Parameters[0],
        param);
    var set = setExpr.Compile();

    // resolve conversion method
    conversion = conversion ?? (x => (TProperty)Convert.ChangeType(x, typeof(TProperty)));

    // convert -> zip -> map
    foreach(var zip in source.Select(conversion).Zip(target, (value, item) => new { item, value })) {
        set(zip.item, zip.value);
    }
}

这个答案的关键部分是如何从 getter 表达式创建一个 setter。

于 2013-05-13T16:52:56.120 回答
0
using System;

class Test
{
    public string P1 { get; set; }

    public string P2 { get; set; }
}

class MainClass
{
    static T MapArray<T>(double[] array, string propertyStartWith) where T: new()
    {
        T obj = new T();
        Type t = typeof(T);
        for (int i = 0; i < array.Length; i++)
        {
            var property = t.GetProperty(propertyStartWith + (i + 1).ToString());
            property.SetValue(obj, Convert.ChangeType(array[i], property.PropertyType), null);
        }
        return obj;
    }

    public static void Main (string[] args)
    {
        double[] d = new double[] {
            Math.PI, Math.E
        };
        Test t = MapArray<Test> (d, "P");
        Console.WriteLine (t.P1);
        Console.WriteLine (t.P2);
    }
}
于 2013-05-13T16:37:22.527 回答