簡體   English   中英

反射屬性.SetValue(

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM