C# Language
Egenskaper
Sök…
Anmärkningar
Egenskaper kombinerar klassens datalagring av fält med metodernas tillgänglighet. Ibland kan det vara svårt att bestämma om man vill använda en egenskap, en egenskap som refererar till ett fält eller en metod som refererar till ett fält. Tumregel:
Egenskaper bör användas utan ett internt fält om de bara får och / eller anger värden; utan någon annan logik. I sådana fall skulle det att tillföra ett internt fält lägga till kod utan någon nytta.
Egenskaper bör användas med interna fält när du behöver manipulera eller validera data. Ett exempel kan vara att ta bort ledande och efterföljande utrymmen från strängar eller se till att ett datum inte är tidigare.
När det gäller Methods vs Properties, där du både kan hämta ( get
) och uppdatera ( set
) ett värde, är en egenskap det bättre valet. Dessutom tillhandahåller .Net mycket funktionalitet som använder klassens struktur; t.ex. att lägga till ett rutnät i ett formulär, .Net kommer som standard att lista alla egenskaper i klassen på det formuläret; så att bäst utnyttja sådana konventioner planerar att använda egenskaper när detta beteende skulle vara vanligtvis önskvärt, och metoder där du föredrar att typerna inte automatiskt läggs till.
Olika egenskaper i sammanhang
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;
}
}
Public Get
Getters används för att exponera värden från klasser.
string name;
public string Name
{
get { return this.name; }
}
Offentlig uppsättning
Sättare används för att tilldela värden till egenskaper.
string name;
public string Name
{
set { this.name = value; }
}
Åtkomst till egenskaper
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)){}
}
Standardvärden för egenskaper
Ställa in ett standardvärde kan göras med hjälp av Initialisatorer (C # 6)
public class Name
{
public string First { get; set; } = "James";
public string Last { get; set; } = "Smith";
}
Om det bara är läst kan du returnera värden som denna:
public class Name
{
public string First => "James";
public string Last => "Smith";
}
Auto-implementerade egenskaper
Auto-implementerade egenskaper introducerades i C # 3.
En autoimplementerad egenskap deklareras med en tom getter och setter (accessors):
public bool IsValid { get; set; }
När en autoimplementerad egenskap skrivs i din kod skapar kompilatorn ett privat anonymt fält som bara kan nås via fastighetens accessorer.
Ovanstående autoimplementerade fastighetsförklaring motsvarar skrivandet av den här långa koden:
private bool _isValid;
public bool IsValid
{
get { return _isValid; }
set { _isValid = value; }
}
Auto-implementerade egenskaper kan inte ha någon logik i sina accessorer, till exempel:
public bool IsValid { get; set { PropertyChanged("IsValid"); } } // Invalid code
En autoimplementerad egenskap kan dock ha olika åtkomstmodifierare för dess accessorer:
public bool IsValid { get; private set; }
C # 6 tillåter att autoimplementerade egenskaper inte har någon setter alls (vilket gör det obrukbart, eftersom dess värde endast kan ställas in i konstruktören eller hårdkodad):
public bool IsValid { get; }
public bool IsValid { get; } = true;
För mer information om initiering av autoimplementerade egenskaper, läs dokumentationen för initierare av automatisk egendom .
Skrivskyddade egenskaper
Deklaration
Ett vanligt missförstånd, speciellt nybörjare, har är läsbar egendom är den som är markerad med readonly
nyckelord. Det är inte korrekt och faktiskt följande är ett sammanställningstidsfel :
public readonly string SomeProp { get; set; }
En egenskap är skrivskyddad när den bara har en getter.
public string SomeProp { get; }
Använda skrivskyddade egenskaper för att skapa immutable klasser
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;
}
}