![](/img/trans.png)
[英]Alternatives to using property.setvalue() for efficiency purposes
[英]Reflection Property.SetValue(
我有以下类可以从分隔行创建对象:
public class Mapper<T>
{
public T Map(string line, char delimiter)
{
if(String.IsNullOrEmpty(line))
throw new ArgumentNullException(nameof(line));
if (Char.IsWhiteSpace(delimiter))
throw new ArgumentException(nameof(delimiter));
var splitString = line.Split(delimiter);
var properties = typeof(T).GetProperties();
if(properties.Count() != splitString.Count())
throw new InvalidOperationException($"Row has {splitString.Count()} columns but object has {properties.Count()}.");
var obj = Activator.CreateInstance<T>();
for (var i = 0; i < splitString.Count(); i++)
{
var prop = properties[i];
var propType = prop.PropertyType;
var valType = Convert.ChangeType(splitString[i], propType);
prop.SetValue(obj, valType);
}
return (T)obj;
}
}
如果我使用分隔字符串调用 map 方法,它将使用行中的分隔值填充对象上的所有属性。
但是,当我从以下调用它时:
public class CsvStreamReader<T>
{
private readonly Mapper<T> _mapper;
public CsvStreamReader(Mapper<T> mapper)
{
_mapper = mapper;
}
public IEnumerable<T> ReadCsvFile(string filePath, bool hasHeader)
{
if(hasHeader)
return File.ReadAllLines(filePath)
.Skip(1)
.Select(x => _mapper.Map(x, ','));
return File.ReadAllLines(filePath)
.Select(x => _mapper.Map(x, ','));
}
}
它将返回一个 T 列表,但所有属性都将为空并且不会被设置。
更新:刚刚意识到我的班级不是班级,而是实际上是一个结构体。
当T
是值类型时,为了使您的Mapper<T>
工作,您需要在装箱为 object 时设置其属性。 使用非通用的Activator.CreateInstance(typeof(T))
将您的obj
创建为object
,使用反射设置其属性,然后在返回时最终将其转换为所需的类型:
public class Mapper<T>
{
readonly List<PropertyInfo> properties = typeof(T).GetProperties().OrderBy(p => p.Name).ToList();
public T Map(string line, char delimiter)
{
if (String.IsNullOrEmpty(line))
throw new ArgumentNullException("line");
if (Char.IsWhiteSpace(delimiter))
throw new ArgumentException("delimiter");
var splitString = line.Split(delimiter);
if (properties.Count() != splitString.Count())
throw new InvalidOperationException(string.Format("Row has {0} columns but object has {1}", splitString.Count(), properties.Count()));
// Create as a reference (boxed if a value type).
object obj = Activator.CreateInstance(typeof(T));
// Set the property values on the object reference.
for (var i = 0; i < splitString.Count(); i++)
{
var prop = properties[i];
var propType = prop.PropertyType;
var valType = Convert.ChangeType(splitString[i], propType);
prop.SetValue(obj, valType);
}
// Cast to the return type unboxing if required.
return (T)obj;
}
}
样品小提琴。
请注意,您的代码不应依赖于Type.GetProperties()
返回的属性顺序。 从文档:
GetProperties 方法不以特定顺序返回属性,例如字母顺序或声明顺序。 您的代码不得依赖于返回属性的顺序,因为该顺序会有所不同。
因此,我修改了您的代码以按名称排序。 您可以选择另一种策略,例如对数据协定类型使用数据成员顺序。
最后,您可能想重新考虑使用可变结构的设计,请参阅为什么可变结构是“邪恶的”? 出于某些原因。 要将您的Mapper<T>
限制为仅适用于引用类型,您可以添加以下where
约束:
public class Mapper<T> where T : class
{
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.