将列表<双[]>转换为列表<T>



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

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

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

是否可以将给定双精度数组的每个索引映射到t类型的类的属性,因此我将List<double[]>转换为List<T>,其中Tclass

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

EDIT:下面给出了一个类的例子

    /// <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:从列表创建列表的方法,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

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

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; }
}

编辑:因为,正如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'
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);
    }
}

这是一个没有错误检查的简单实现,但是您可以指定自己的转换函数。目前它可以转换。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

相关内容

  • 没有找到相关文章