首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解析CSV文件中的仪表读数

解析CSV文件中的仪表读数
EN

Code Review用户
提问于 2021-09-05 09:35:47
回答 1查看 139关注 0票数 1

我有一堂表示表读数的课:

代码语言:javascript
复制
public class MeterReading
    {
        public double AccountId { get; set; }
        public DateTime MeterReadingDateTime { get; set; }
        public double MeterReadValue { get; set; }
    }

我还收集了一个名为MeterReadingsMeterReadingParseResult

代码语言:javascript
复制
public class MeterReadingsParseResult : MeterReadingsBase
{
}

这是从MeterReadingBase继承的。

代码语言:javascript
复制
public class MeterReadingsBase
{
    private List meterReadings = new List();
    public List MeterReadings { get => meterReadings; set => meterReadings = value; }
    public int FailedParseReadings { get; set; }
}

我所关注的主要方法是使用一个IFormFile (即CSV ),并在CSVHelper NuGet包的帮助下验证CSV文件内容,将其传递到D6类并将其添加到D6 MeterReadingParseResult集合中。CSVHelper代码通常是几行代码,但有些CSV文件可能包含需要加倍的单词,因此我不得不定制它,使验证符合我的目的。

代码语言:javascript
复制
   public class MeterReadingsCsvProcessor
    {
        private readonly IMeterReadingsRepository _meterReadingsRepository;
        private readonly IAccountsRepository _accountsRepository;
        private MeterReadingsParseResult _parseResult;

        public MeterReadingsCsvProcessor(
            IMeterReadingsRepository meterReadingsRepository, IAccountsRepository accountsRepository)
        {
            _meterReadingsRepository = meterReadingsRepository;
            _accountsRepository = accountsRepository;
            _parseResult = new MeterReadingsParseResult();
        }

        public MeterReadingsParseResult CsvHandler(IFormFile file)
        {
            if (file == null)
                throw new ArgumentNullException(nameof(file));

            ParseCsv(file);

            var accounts = _accountsRepository.GetAccounts();

            return _parseResult;
        }


        private void ParseCsv(IFormFile file)
        {
            TextReader textReader = new StreamReader(file.OpenReadStream());
            using (var csv = new CsvReader(textReader, CultureInfo.InvariantCulture))
            {
                csv.Read();
                csv.ReadHeader();

                CsvCustomErrorHandler customInt32Converter = new CsvCustomErrorHandler();

                int lineNumber = 0;

                while (csv.Read())
                {
                    lineNumber++;
                    var meterReading = new MeterReading();
                    {
                        meterReading.AccountId = csv.GetField("AccountId");

                        meterReading.MeterReadingDateTime = DateTime.ParseExact
                            (csv.GetField("MeterReadingDateTime"),
                            "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);

                        meterReading.MeterReadValue = csv.GetField("MeterReadValue", customInt32Converter);
                        if (meterReading.MeterReadValue < 1 || meterReading.MeterReadValue >= 100000)
                        {
                            _parseResult.FailedParseReadings++;
                            continue;
                        }
                    }
                    _parseResult.MeterReadings.Add(meterReading);

                }
            }
        }
    }

我是否可以重构这段代码,以使它看起来更干净,或者在您的意见中,您认为我在我需要实现的功能方面做得最好吗?

EN

回答 1

Code Review用户

发布于 2021-09-06 11:07:34

1.记录您的代码

虽然现在做起来有点无聊和无用,但在6个月后,您将再次阅读您的代码,并且需要一些时间才能记住这个类/字段/属性用于什么.

最低限度是用XML注释记录所有公共部分。您可以键入三个斜杠,///和Visual将插入以下内容:

代码语言:javascript
复制
/// 
/// 
/// 

总之,您可以为要记录的项插入简短的说明。这在Visual中是可见的,并且可能非常有用。

属性一致性

在任何地方都可以使用没有私有字段的自动属性。除了一次,为了一份清单。为什么它不是另一个自动财产呢?

代码语言:javascript
复制
public List MeterReadings { get; set; } = new List();

字段命名约定

在某种程度上,私有字段以小写字母开头。在其他地方,您可以用下划线开始您的私有字段。保持它的一致性,总是按照相同的惯例写它们。没有严格的规则,所以你只需要与自己保持一致(这并不总是容易的)或你的团队(这更难)。

在个人问题上,我不喜欢下划线,因为编译器使用它在后台生成一些支持字段,我不想干扰这一点。

4.未使用的变量

你有未使用的变量。这可能是一个良好的启动代码清理。你写的每一个变量都会花你一些时间去阅读和理解它的存在。如果没有使用变量,那么您就会浪费时间来理解为什么不使用它.

也许它在您的代码的其他部分中使用,但是由于您没有提到它,我假设它不会在其他地方使用。

  • _meterReadingsRepository:在构造函数中设置它,然后永远不再。是因为构造函数应该是这样的吗?因为它来自另一个你无法控制的公共接口?这是可能发生的,但请注意,这样您就可以知道为什么要浪费时间来创建一个不使用的变量。
  • int lineNumber = 0;:您设置了这个值,并在每次迭代时增加它。非常好。但是你从来没有读过这个值,那有什么意义呢?调试问题?
  • private MeterReadingsParseResult _parseResult;:您可以使用这个变量来存储csv解析结果,这很好。但你再也不用它了。所以你会记住它,也许是为了节省下一次调用解析方法时的时间?但你还是重新创造了它!所以变量是完全无用的。
  • var accounts = _accountsRepository.GetAccounts();:这个简直就像魔法.这个电话该怎么做?因为你从不使用accounts变量。也许方法本身能为其他人做些重要的事情,但是没有办法知道.

5.类型一致性

字段AccountId & MeterReadValue是类型为double,但是当您从csv解析它们时,它们应该是int。为什么?它们应该是整数还是双倍?

如果它们应该是双的,那么您就有一个bug,您应该将它们解析为double而不是int。

如果它们应该是int,则不需要使属性加倍。除非您有真正的理由,否则您应该添加一个注释来解释为什么它们是双重的,但被解析为int。

空检查

检查一次null。这很好。但是休息呢。您是否应该在构造函数中检查空值,在构造函数中接收两个值并将它们分配给您的私有字段?

7.方法的工作

CsvHandler到底在做什么?

  • 首先,它检查文件是否为空。
  • 然后调用实际执行某些工作的方法。
  • 然后你称之为一种没有人知道它做什么的方法。
  • 最后,返回字段值。

这个方法值得吗?

另一个注释将出现在该方法的名称上。通常,你会选择一个动词来开始一个方法名,以便知道这个方法应该做什么.

以下是我应该做的:

代码语言:javascript
复制
public class MeterReading
{
    public double AccountId { get; set; }
    public DateTime MeterReadingDateTime { get; set; }
    public double MeterReadValue { get; set; }
}

public class MeterReadingsBase
{
    public List MeterReadings { get; set; } = new List();
    public int FailedParseReadings { get; set; }
}

public class MeterReadingsParseResult : MeterReadingsBase
{
}

public class MeterReadingsCsvProcessor
{
    /// 
    /// Implements a new meter reading csv processor.
    /// 
    /// Unused parameter. Kept for retro compatibility
    /// Unused parameter. Kept for retro compatibility
    public MeterReadingsCsvProcessor(IMeterReadingsRepository meterReadingsRepository, IAccountsRepository accountsRepository)
    {
    }

    public MeterReadingsParseResult ParseCsvFile(IFormFile file)
    {
        if (file == null)
            throw new ArgumentNullException(nameof(file));

        using (TextReader textReader = new StreamReader(file.OpenReadStream()))
        using (CsvReader csv = new CsvReader(textReader, CultureInfo.InvariantCulture))
        {
            return ParseCsv(csv);
        }
    }


    private MeterReadingsParseResult ParseCsv(CsvReader csv)
    {
        MeterReadingsParseResult parseResult = new MeterReadingsParseResult();
        csv.Read();
        csv.ReadHeader();

        CsvCustomErrorHandler customInt32Converter = new CsvCustomErrorHandler();

        while (csv.Read())
        {
            MeterReading reading = ReadOneValue(csv, customInt32Converter);
            if (reading == null)
            {
                parseResult.FailedParseReadings++;
            }
            else
            {
                parseResult.MeterReadings.Add(reading);
            }
        }
        return parseResult;
    }

    private MeterReading ReadOneValue(CsvReader csv, CsvCustomErrorHandler customInt32Converter)
    {
        MeterReading reading = new MeterReading()
        {
            AccountId = csv.GetField("AccountId"),
            MeterReadingDateTime = DateTime.ParseExact(csv.GetField("MeterReadingDateTime"), "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture),
            MeterReadValue = csv.GetField("MeterReadValue", customInt32Converter)
        };
        if (reading.MeterReadValue < 1 || reading.MeterReadValue >= 100000)
        {
            return null;
        }
        return reading;
    }
}
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/267705

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档