Компилятор не различает конструктор по умолчанию и конструктор с ключевым словом params

Суть проблемы заключается в том, что если я создаю объект Progression с пустыми скобками в конце, объект создается по конструктору (params float[]), вместо выдачи ошибки, несмотря на то что конструктор по умолчанию я сделал приватным.

class Progression
    {
        public float FirstElement { get; }
        public float Difference { get; }

        private Progression()
        {
            
        }

        public Progression (float firstEl, float d)
        {
            FirstElement = firstEl;
            Difference = d;
        }

        public Progression (float firstEl, (float, int) randomEl)
        {
            FirstElement = firstEl;
            Difference = (randomEl.Item1 - firstEl) / (randomEl.Item2 - 1);
        }

        public Progression (Func<int, float> formula)
        {
            FirstElement = formula(1);
            Difference = formula(2) - formula(1);
        }

        public Progression (params float[] element)
        {
            FirstElement = element[0];
            Difference = element[1] - element[0];
        }
    }

Ответы (1 шт):

Автор решения: VladD

Так работает params: пустой массив является частным случаем массива. Поэтому ваш вызов не может быть обслужен приватным конструктором (потому что он приватный), но может быть обслужен конструктором с params.

Если вы хотите, чтобы хотя бы один параметр в params был обязательным, вынесите его из params:

public Progression(float firstEl, params float[] restElements)
{
    FirstElement = firstEl;
    Difference = restElements[0] - firstEl;
}

Кстати, у вас и так есть конструктор с двумя float-аргументами, так что вызов new Progression(1f, 1f) вызовет конструктор Progression(float firstEl, float d), а не Progression(float firstEl, params float[] restElements). Это явно не то, что вам нужно, т. к. второй элемент будет проинтерпретирован не как член последовательности, а как разность.

А вот вызов конструктора с одним аргументом снова приведёт к пустому массиву и падению на restElements[0]. Так что вам по сути нужно два обязательных параметра.


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

class Progression
{
    public float FirstElement { get; }
    public float Difference { get; }

    private Progression(float firstEl, float d)
    {
        FirstElement = firstEl;
        Difference = d;
    }

    static public Progression FromStartAndDiff(float start, float diff)
    {
        return new Progression(start, diff);
    }

    static public Progression FromFormula(Func<int, float> formula)
    {
        return new Progression(formula(1), formula(2) - formula(1));
    }

    static public Progression FromElements(float firstEl, float secondEl)
    {
        return new Progression(firstEl, secondEl - firstEl);
    }
}

При этом вы можете создавать объект так:

var p1 = Progression.FromStartAndDiff(1, 2);
var p2 = Progression.FromFormula(n => 2 * n - 1);
var p3 = Progression.FromElements(1, 3);
→ Ссылка