Использовать поле stati c только для чтения в конструкторе C# - PullRequest
0 голосов
/ 10 апреля 2020

Ниже мой класс с полем c readonly:

public class Stats
{
    public static readonly int MinPoints = 900;
    public static readonly int MaxPoints = 1500;

    public int Points { get; }

    private Stats()
    {
    }

    public Stats(int points) : this()
    {
        if (points < Stats.MinPoints || points > Stats.MaxPoints)
        {
            throw new InvalidOperationException();
        }

        Points = points;
    }
}

При компиляции у меня нет ошибок. Но когда я пытаюсь создать экземпляр объекта Stats, я получаю TypeInitializationException. При замене PlayerStatistics.MaxPoints в конструкторе на правильное значение код работает.

Вот ошибка, которую я получаю при модульном тестировании конструктора:

Expected a <System.InvalidOperationException> to be thrown, but found <System.TypeInitializationException>: "
"System.TypeInitializationException with message "The type initializer for 'League.Domain.Player.Stats' threw an exception."
     at League.Domain.Player.Stats..ctor(Int32 points) in C:\dev\repos\League\League.Domain\Player\Stats.cs:line 24
     at League.Tests.StatsSpecs.<>c__DisplayClass1_0.<Create_stats_with_incorrect_points_should_throw_exception>b__0() in C:\dev\repos\League\League.Tests\StatsSpecs.cs:line 36
     at FluentAssertions.Specialized.ActionAssertions.InvokeSubject()
     at FluentAssertions.Specialized.DelegateAssertions`1.InvokeSubjectWithInterception()
.
   at FluentAssertions.Execution.XUnit2TestFramework.Throw(String message)
   at FluentAssertions.Execution.TestFrameworkProvider.Throw(String message)
   at FluentAssertions.Execution.DefaultAssertionStrategy.HandleFailure(String message)
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(String message, Object[] args)
   at FluentAssertions.Specialized.DelegateAssertions`1.Throw[TException](Exception exception, String because, Object[] becauseArgs)
   at FluentAssertions.Specialized.DelegateAssertions`1.Throw[TException](String because, Object[] becauseArgs)
   at League.Tests.StatsSpecs.Create_stats_with_incorrect_points_should_throw_exception(Int32 points) in C:\dev\repos\League\League.Tests\StatsSpecs.cs:line 38

А вот мой модульный тест , написанный с помощью XUnit:

[Theory]
[InlineData(899)]
[InlineData(1501)]
public void Create_stats_with_incorrect_points_should_throw_exception(int points)
{
    Action action = () => new Stats(points);

    action.Should().Throw<InvalidOperationException>();
}

Есть ли способ по-прежнему использовать поле stati c readonly в конструкторе?

1 Ответ

0 голосов
/ 11 апреля 2020

Мне удалось найти решение ...

Я пытался слишком сильно упростить свой код и не указал строку в моем классе:

public class Stats
{
    // I didn't put this line of code, so it was working without it
    public static readonly Stats New = new Stats(1000);

    public static readonly int MinPoints = 900;
    public static readonly int MaxPoints = 1500;

    public int Points { get; }

    private Stats()
    {
    }

    public Stats(int points) : this()
    {
        if (points < Stats.MinPoints || points > Stats.MaxPoints)
        {
            throw new InvalidOperationException();
        }

        Points = points;
    }
}

Итак, как уже упоминалось Дмитрий Быченко, поле stati c readonly не присваивается во время компиляции. Так что, если бы я поместил эти строки перед пропущенной строкой, например:

public static readonly int MinPoints = 900;
public static readonly int MaxPoints = 1500;

public static readonly Stats New = new Stats(1000);

Это бы хорошо работало.

И благодаря его комментарию, используя const вместо stati c поле только для чтения, чтобы мой код работал, в любом месте, где я поместил эти две константы в моем классе.

...