Szukaj…


Uwagi

Właściwości łączą klasowe przechowywanie danych pól z dostępnością metod. Czasami może być trudno zdecydować, czy użyć właściwości, właściwości odnoszącej się do pola, czy metody odnoszącej się do pola. Jako zasada:

  • Właściwości powinny być używane bez wewnętrznego pola, jeśli tylko pobierają i / lub ustawiają wartości; bez innej logiki. W takich przypadkach dodanie pola wewnętrznego oznaczałoby dodanie kodu bez korzyści.

  • Właściwości należy używać z polami wewnętrznymi, gdy trzeba manipulować lub sprawdzać poprawność danych. Przykładem może być usunięcie początkowych i końcowych spacji z ciągów znaków lub upewnienie się, że data nie jest przeszłością.

Jeśli chodzi o metody kontra właściwości, w których można zarówno pobrać ( get ), jak i zaktualizować ( set ) wartość, właściwość jest lepszym wyborem. Ponadto .Net zapewnia wiele funkcji korzystających ze struktury klasy; np. dodając siatkę do formularza, .Net domyślnie wyświetli listę wszystkich właściwości klasy w tym formularzu; dlatego, aby jak najlepiej wykorzystać takie konwencje, planuj używać właściwości, gdy takie zachowanie byłoby zwykle pożądane, oraz metod, w których wolisz, aby typy nie były automatycznie dodawane.

Różne właściwości w kontekście

public class Person 
{
    //Id property can be read by other classes, but only set by the Person class
    public int Id {get; private set;}
    //Name property can be retrieved or assigned 
    public string Name {get; set;}
    
    private DateTime dob;
    //Date of Birth property is stored in a private variable, but retrieved or assigned through the public property.
    public DateTime DOB
    {
        get { return this.dob; }
        set { this.dob = value; }
    }
    //Age property can only be retrieved; it's value is derived from the date of birth 
    public int Age 
    {
        get 
        {
            int offset = HasHadBirthdayThisYear() ? 0 : -1;
            return DateTime.UtcNow.Year - this.dob.Year + offset;
        }
    }

    //this is not a property but a method; though it could be rewritten as a property if desired.
    private bool HasHadBirthdayThisYear() 
    {
        bool hasHadBirthdayThisYear = true;
        DateTime today = DateTime.UtcNow;
        if (today.Month > this.dob.Month)
        {
            hasHadBirthdayThisYear = true;
        }
        else 
        {
            if (today.Month == this.dob.Month)
            {
                hasHadBirthdayThisYear = today.Day > this.dob.Day;
            }
            else
            {
                hasHadBirthdayThisYear = false;
            }
        }
        return hasHadBirthdayThisYear;
    }
}

Publiczne Get

Do pobierania wartości z klas służą gettery.

string name;
public string Name
{
    get { return this.name; }
}

Zestaw publiczny

Settery służą do przypisywania wartości do właściwości.

string name;
public string Name 
{
    set { this.name = value; }
}

Dostęp do właściwości

class Program 
{
    public static void Main(string[] args)
    {
        Person aPerson = new Person("Ann Xena Sample", new DateTime(1984, 10, 22));
        //example of accessing properties (Id, Name & DOB)
        Console.WriteLine("Id is:  \t{0}\nName is:\t'{1}'.\nDOB is: \t{2:yyyy-MM-dd}.\nAge is: \t{3}", aPerson.Id, aPerson.Name, aPerson.DOB, aPerson.GetAgeInYears());
        //example of setting properties

        aPerson.Name = "   Hans Trimmer  ";
        aPerson.DOB = new DateTime(1961, 11, 11);
        //aPerson.Id = 5; //this won't compile as Id's SET method is private; so only accessible within the Person class.
        //aPerson.DOB = DateTime.UtcNow.AddYears(1); //this would throw a runtime error as there's validation to ensure the DOB is in past. 

        //see how our changes above take effect; note that the Name has been trimmed
        Console.WriteLine("Id is:  \t{0}\nName is:\t'{1}'.\nDOB is: \t{2:yyyy-MM-dd}.\nAge is: \t{3}", aPerson.Id, aPerson.Name, aPerson.DOB, aPerson.GetAgeInYears());

        Console.WriteLine("Press any key to continue");
        Console.Read();
    }
}

public class Person
{
    private static int nextId = 0;
    private string name;
    private DateTime dob; //dates are held in UTC; i.e. we disregard timezones
    public Person(string name, DateTime dob)
    {
        this.Id = ++Person.nextId;
        this.Name = name;
        this.DOB = dob;
    }
    public int Id
    {
        get;
        private set;
    }
    public string Name
    {
        get { return this.name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value)) throw new InvalidNameException(value);
            this.name = value.Trim();
        }
    }
    public DateTime DOB
    {
        get { return this.dob; }
        set 
        {
            if (value < DateTime.UtcNow.AddYears(-200) || value > DateTime.UtcNow) throw new InvalidDobException(value);
            this.dob = value; 
        }
    }
    public int GetAgeInYears()
    {
        DateTime today = DateTime.UtcNow;
        int offset = HasHadBirthdayThisYear() ? 0 : -1;
        return today.Year - this.dob.Year + offset;
    }
    private bool HasHadBirthdayThisYear()
    {
        bool hasHadBirthdayThisYear = true;
        DateTime today = DateTime.UtcNow;
        if (today.Month > this.dob.Month)
        {
            hasHadBirthdayThisYear = true;
        }
        else
        {
            if (today.Month == this.dob.Month)
            {
                hasHadBirthdayThisYear = today.Day > this.dob.Day;
            }
            else
            {
                hasHadBirthdayThisYear = false;
            }
        }
        return hasHadBirthdayThisYear;
    }
}

public class InvalidNameException : ApplicationException
{
    const string InvalidNameExceptionMessage = "'{0}' is an invalid name.";
    public InvalidNameException(string value): base(string.Format(InvalidNameExceptionMessage,value)){}
}
public class InvalidDobException : ApplicationException
{ 
    const string InvalidDobExceptionMessage = "'{0:yyyy-MM-dd}' is an invalid DOB.  The date must not be in the future, or over 200 years in the past.";
    public InvalidDobException(DateTime value): base(string.Format(InvalidDobExceptionMessage,value)){}
}

Domyślne wartości właściwości

Ustawienie wartości domyślnej można wykonać za pomocą Inicjatorów (C # 6)

public class Name 
{
    public string First { get; set; } = "James";
    public string Last { get; set; } = "Smith";
}

Jeśli jest tylko do odczytu, możesz zwrócić takie wartości:

  public class Name 
  {
      public string First => "James";
      public string Last => "Smith";
  }

Właściwości zaimplementowane automatycznie

Automatycznie zaimplementowane właściwości zostały wprowadzone w C # 3.
Automatycznie zaimplementowana właściwość jest zadeklarowana z pustym narzędziem pobierającym i ustawiającym (akcesoriami):

public bool IsValid { get; set; }

Gdy w kodzie zapisywana jest automatycznie zaimplementowana właściwość, kompilator tworzy prywatne anonimowe pole, do którego można uzyskać dostęp tylko za pośrednictwem akcesorów właściwości.

Powyższa instrukcja właściwości zaimplementowana automatycznie odpowiada zapisaniu tego długiego kodu:

private bool _isValid;
public bool IsValid
{
    get { return _isValid; }
    set { _isValid = value; }
}

Automatycznie zaimplementowane właściwości nie mogą mieć logiki w swoich akcesoriach, na przykład:

public bool IsValid { get; set { PropertyChanged("IsValid"); } } // Invalid code

Automatycznie zaimplementowana właściwość może jednak mieć różne modyfikatory dostępu dla swoich akcesorów:

public bool IsValid { get; private set; }    

C # 6 pozwala, aby automatycznie zaimplementowane właściwości w ogóle nie miały setera (co czyni go niezmiennym, ponieważ jego wartość może być ustawiona tylko wewnątrz konstruktora lub zakodowana na stałe):

public bool IsValid { get; }    
public bool IsValid { get; } = true;

Aby uzyskać więcej informacji na temat inicjowania właściwości automatycznie zaimplementowanych, przeczytaj dokumentację inicjalizującą właściwości automatyczne .

Właściwości tylko do odczytu

Deklaracja

Częstym nieporozumieniem, szczególnie dla początkujących, jest właściwość tylko do readonly oznaczona słowem kluczowym readonly do readonly . To nie jest poprawne i w rzeczywistości następujący błąd kompilacji :

public readonly string SomeProp { get; set; }

Właściwość jest tylko do odczytu, gdy ma tylko getter.

public string SomeProp { get; }

Używanie właściwości tylko do odczytu do tworzenia niezmiennych klas

public Address
{
    public string ZipCode { get; }
    public string City { get; }
    public string StreetAddress { get; }

    public Address(
        string zipCode,
        string city,
        string streetAddress)
    {
        if (zipCode == null)
            throw new ArgumentNullException(nameof(zipCode));
        if (city == null)
            throw new ArgumentNullException(nameof(city));
        if (streetAddress == null)
            throw new ArgumentNullException(nameof(streetAddress));

        ZipCode = zipCode;
        City = city;
        StreetAddress = streetAddress;
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow