我有一个双精度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>
,其中T
为class
。
我知道它可以通过手动遍历列表读取每个数组,然后从数组的每个索引读取值,并将其传递给类的相应属性。但是当我有很多类有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