C# 类构造函数,该构造函数要求命名所有参数和所有参数

C# 类构造函数,该构造函数要求命名所有参数和所有参数,c#,initialization,C#,Initialization,有没有一种方法可以定义我的类,这样该类的调用者就会得到编译时错误,除非他们指定了该类的每个属性,并附加了参数传递的命名约束 假设我有这个界面: public interface IPerson { string FirstName {get;} string MiddleName { get; } string LastName { get;} } 这些课程: public class PersonNeedAllProperties : IPerson { pu

有没有一种方法可以定义我的类,这样该类的调用者就会得到编译时错误,除非他们指定了该类的每个属性,并附加了参数传递的命名约束

假设我有这个界面:

public interface IPerson
{
    string FirstName {get;}
    string MiddleName { get; }
    string LastName { get;}
}
这些课程:

public class PersonNeedAllProperties : IPerson
{
    public string FirstName { get; private set;} // ideally readonly
    public string MiddleName { get; private set;}
    public string LastName { get; private set;}

    public PersonNeedAllProperties(
        string firstName,
        string lastName,
        string middleName)
    {
        this.FirstName = firstName;
        this.MiddleName = middleName;
        this.LastName = lastName;
    }
}

public class PersonCanNamePropertiesButNotSadlyNotRequired : IPerson
{
    public string FirstName { get; set;} // ideally readonly
    public string MiddleName { get; set;}
    public string LastName { get; set;}
}
那么这些imho的问题如下:

var p1 = new PersonNeedAllProperties("lastName", "firstName", "middle");
 // woops wrong order because not named.

var p2 = new PersonCanNamePropertiesButNotSadlyNotRequired()
{
    FirstName = "firstName",
    LastName = "lastName"
}; // woops forgot all about the middlename.
有没有一种方法可以获得一个初始化类似于person 2但需要命名所有属性的简单POCO对象


这是上面的问题:

回答你的问题不,这是不可能的

你要求能够强迫两件事:

  • a) 强制设置所有属性
  • b) 强制命名属性
这些事情可以单独完成,但它们不能结合在一起,因为这样做的方法是截然相反的,如下所述

a) 要强制设置所有属性,需要将它们作为构造函数中的输入

public abstract class Person
{
    public abstract string FirstName {get; protected set; }
    public abstract string MiddleName { get; protected set; }
    public abstract string LastName { get; protected set; }

    public Person(string firstName, string middleName, string lastName){
        FirstName = firstName;
        MiddleName = middleName;
        LastName = lastName;
    }
}

public class PersonNeedAllProperties : Person
{
    public override string FirstName{get; protected set;}
    public override string MiddleName{get; protected set;}
    public override string LastName{get; protected set;}

    public PersonNeedAllProperties(
        string firstName,
        string lastName,
        string middleName)
        : base(firstName, lastName, middleName)
    {
    }
}
b) 要强制按名称设置属性,不能在构造函数中指定它们

旁注:可以向构造函数提供命名参数,但不能强制这些参数,即
var person=new person(firstName:'John',lastName:'Doe',middleName:'a')

最接近的方法是将属性标记为只读(私有集),并像在
PersonneAllProperties
类中那样,仅从构造函数中设置属性

可能是一种可行的替代方案的东西被称为。
这将有一个额外的类负责构造对象,但您仍然无法得到编译时错误,只能得到运行时错误。

回答您的问题否,这是不可能的

你要求能够强迫两件事:

  • a) 强制设置所有属性
  • b) 强制命名属性
这些事情可以单独完成,但它们不能结合在一起,因为这样做的方法是截然相反的,如下所述

a) 要强制设置所有属性,需要将它们作为构造函数中的输入

public abstract class Person
{
    public abstract string FirstName {get; protected set; }
    public abstract string MiddleName { get; protected set; }
    public abstract string LastName { get; protected set; }

    public Person(string firstName, string middleName, string lastName){
        FirstName = firstName;
        MiddleName = middleName;
        LastName = lastName;
    }
}

public class PersonNeedAllProperties : Person
{
    public override string FirstName{get; protected set;}
    public override string MiddleName{get; protected set;}
    public override string LastName{get; protected set;}

    public PersonNeedAllProperties(
        string firstName,
        string lastName,
        string middleName)
        : base(firstName, lastName, middleName)
    {
    }
}
b) 要强制按名称设置属性,不能在构造函数中指定它们

旁注:可以向构造函数提供命名参数,但不能强制这些参数,即
var person=new person(firstName:'John',lastName:'Doe',middleName:'a')

最接近的方法是将属性标记为只读(私有集),并像在
PersonneAllProperties
类中那样,仅从构造函数中设置属性

可能是一种可行的替代方案的东西被称为。
这将有一个额外的类负责构造对象,但您仍然无法得到编译时错误,只能得到运行时错误。

有一种可怕的方法几乎可以实现这一点。不是按名字而是按类型。我不推荐。但是有一种可怕的方法。我说过这很可怕吗

“计算机科学中的任何问题都可以通过增加一层间接性来解决”——惠勒

使每个属性都具有各自不同的类型。现在不能以错误的顺序传递它们,否则编译器会告诉您传递了无效的类型

public class FirstName
{
    string Value { get; set; }
}

public class MiddleName
{
    string Value { get; set; }
}

public class LastName
{
    string Value { get; set; }
}

public interface IPerson
{
    FirstName FirstName {get;}
    MiddleName MiddleName { get; }
    LastName LastName { get;}
}

public class PersonNeedAllProperties : IPerson
{
    public FirstName FirstName { get; private set;} // ideally readonly
    public MiddleName MiddleName { get; private set;}
    public LastName LastName { get; private set;}

    public PersonNeedAllProperties(
        FirstName firstName,
        MiddleName lastName,
        LastName middleName)
    {
        this.FirstName = firstName;
        this.MiddleName = middleName;
        this.LastName = lastName;
    }
}

有一种可怕的方法几乎可以强制执行。不是按名字而是按类型。我不推荐。但是有一种可怕的方法。我说过这很可怕吗

“计算机科学中的任何问题都可以通过增加一层间接性来解决”——惠勒

使每个属性都具有各自不同的类型。现在不能以错误的顺序传递它们,否则编译器会告诉您传递了无效的类型

public class FirstName
{
    string Value { get; set; }
}

public class MiddleName
{
    string Value { get; set; }
}

public class LastName
{
    string Value { get; set; }
}

public interface IPerson
{
    FirstName FirstName {get;}
    MiddleName MiddleName { get; }
    LastName LastName { get;}
}

public class PersonNeedAllProperties : IPerson
{
    public FirstName FirstName { get; private set;} // ideally readonly
    public MiddleName MiddleName { get; private set;}
    public LastName LastName { get; private set;}

    public PersonNeedAllProperties(
        FirstName firstName,
        MiddleName lastName,
        LastName middleName)
    {
        this.FirstName = firstName;
        this.MiddleName = middleName;
        this.LastName = lastName;
    }
}

不,接口不是设计成这样的

接口确保实现它的类将在接口中声明所有元素。但没有就如何实现这些目标发表任何声明

您可以使用带有自定义构造函数的抽象类来完成所需的任务

public abstract class Person
{
    public abstract string FirstName {get; protected set; }
    public abstract string MiddleName { get; protected set; }
    public abstract string LastName { get; protected set; }

    public Person(string firstName, string middleName, string lastName){
        FirstName = firstName;
        MiddleName = middleName;
        LastName = lastName;
    }
}

public class PersonNeedAllProperties : Person
{
    public override string FirstName{get; protected set;}
    public override string MiddleName{get; protected set;}
    public override string LastName{get; protected set;}

    public PersonNeedAllProperties(
        string firstName,
        string lastName,
        string middleName)
        : base(firstName, lastName, middleName)
    {
    }
}
自定义构造函数强制您完成子类中的名称

您可以将这个答案与使用类而不是字符串作为名称(firstName、middleName、lastName)来模拟命名参数的答案结合起来


您需要了解此方法的局限性,在C语言中,您不能从多个类继承,因此,如果您决定实现此解决方案,则需要记住此限制。

不,接口的设计不是这样的

接口确保实现它的类将在接口中声明所有元素。但没有就如何实现这些目标发表任何声明

您可以使用带有自定义构造函数的抽象类来完成所需的任务

public abstract class Person
{
    public abstract string FirstName {get; protected set; }
    public abstract string MiddleName { get; protected set; }
    public abstract string LastName { get; protected set; }

    public Person(string firstName, string middleName, string lastName){
        FirstName = firstName;
        MiddleName = middleName;
        LastName = lastName;
    }
}

public class PersonNeedAllProperties : Person
{
    public override string FirstName{get; protected set;}
    public override string MiddleName{get; protected set;}
    public override string LastName{get; protected set;}

    public PersonNeedAllProperties(
        string firstName,
        string lastName,
        string middleName)
        : base(firstName, lastName, middleName)
    {
    }
}
自定义构造函数强制您完成子类中的名称

您可以将这个答案与使用类而不是字符串作为名称(firstName、middleName、lastName)来模拟命名参数的答案结合起来


您需要了解此方法的局限性,在C中,您不能从多个类继承,因此,如果您决定实现此解决方案,则需要记住此限制。

这可以通过嵌套类实现,我想:

public interface IPerson
{
    string FirstName { get; }
    string MiddleName { get; }
    string LastName { get; }
}

public class Person : IPerson
{
    public string FirstName { get; private set; }
    public string MiddleName { get; private set; }
    public string LastName { get; private set; }

    //Make sure the parameterless constructor is private
    private Person() { }

    private Person(string first, string middle, string last)
    {
        this.FirstName = first;
        this.MiddleName = middle;
        this.LastName = last;
    }

    public class Builder
    {
        private Person person = new Person();

        public Builder WithFirstName(string first)
        {
            person.FirstName = first;
            return this;
        }

        public Builder WithMiddleName(string middle)
        {
            person.MiddleName = middle;
            return this;
        }

        public Builder WithLastName(string last)
        {
            person.LastName = last;
            return this;
        }

        public IPerson Build()
        {
            if (person.FirstName != null
                && person.MiddleName != null
                && person.LastName != null)

                return person;

            throw new Exception("Cannot build person because reasons...");
        }
    }
}
然后按如下方式使用:

var person = new Person.Builder()
                    .WithFirstName("Rob")
                    .WithMiddleName("<I have no middle name>")
                    .WithLastName("Janssen")
                    .Build();


编辑:废话;我没有注意到编译时错误的其他要求

但是,这会“强制”您设置所有3个必填字段,并“强制”您使用
With…
方法“命名”字段,这使得很难混淆值,并且允许您按照所需的顺序指定值。它还可以防止您自己实例化一个
,并允许您拥有自己的私有se