수색…
비고
속성은 필드의 클래스 데이터 저장소와 메서드의 액세스 가능성을 결합합니다. 때로는 속성, 필드를 참조하는 속성 또는 필드를 참조하는 메서드를 사용할지 여부를 결정하기가 어려울 수 있습니다. 경험 법칙 :
값을 가져 오거나 설정하는 경우에만 내부 필드없이 속성을 사용해야합니다. 다른 논리는 발생하지 않습니다. 이 경우 내부 필드를 추가하면 아무런 이점도없는 코드가 추가됩니다.
데이터를 조작하거나 유효성을 검사해야하는 경우 속성을 내부 필드와 함께 사용해야합니다. 예를 들어 문자열에서 앞뒤 공백을 제거하거나 날짜가 과거가 아님을 확인할 수 있습니다.
메소드 vs 속성과 관련하여 값을 검색 ( get
) 및 업데이트 ( set
) 할 수있는 곳에서는 속성이 더 나은 선택입니다. 또한 .Net은 클래스의 구조를 사용하는 많은 기능을 제공합니다. 예를 들어 폼에 그리드를 추가하면 .Net은 기본적으로 해당 폼에있는 클래스의 모든 속성을 나열합니다. 따라서 이러한 동작이 일반적으로 바람직 할 때 속성을 사용할 계획 인 이러한 규칙을 최대한 활용하고 유형이 자동으로 추가되지 않도록하는 방법을 선호합니다.
상황에 따른 다양한 속성
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;
}
}
공개 Get
getter는 클래스의 값을 노출하는 데 사용됩니다.
string name;
public string Name
{
get { return this.name; }
}
공개 세트
setter는 속성에 값을 할당하는 데 사용됩니다.
string name;
public string Name
{
set { this.name = value; }
}
속성에 액세스하기
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)){}
}
속성의 기본값
기본값 설정은 초기화 프로그램 (C # 6)을 사용하여 수행 할 수 있습니다.
public class Name
{
public string First { get; set; } = "James";
public string Last { get; set; } = "Smith";
}
읽기 전용 인 경우 다음과 같은 값을 반환 할 수 있습니다.
public class Name
{
public string First => "James";
public string Last => "Smith";
}
자동 구현 속성
자동 구현 속성 은 C # 3에 도입되었습니다.
자동 구현 된 속성은 빈 getter 및 setter (접근 자)로 선언됩니다.
public bool IsValid { get; set; }
자동 구현 된 속성이 코드에 작성되면 컴파일러는 속성의 접근자를 통해서만 액세스 할 수있는 비공개 익명 필드를 만듭니다.
위의 자동 구현 속성 문은이 긴 코드를 작성하는 것과 같습니다.
private bool _isValid;
public bool IsValid
{
get { return _isValid; }
set { _isValid = value; }
}
자동 구현 된 속성은 접근 자에 어떠한 논리도 가질 수 없습니다. 예를 들면 다음과 같습니다.
public bool IsValid { get; set { PropertyChanged("IsValid"); } } // Invalid code
자동 구현 된 속성 은 접근 자에 대해 다른 액세스 한정자를 가질 수 있습니다 .
public bool IsValid { get; private set; }
C # 6에서는 자동 구현 된 속성에 설정자가 없으므로 (값을 생성자 내부에서만 설정하거나 하드 코드로 설정할 수 있으므로 불변으로 만듭니다).
public bool IsValid { get; }
public bool IsValid { get; } = true;
자동 구현 된 속성을 초기화하는 방법에 대한 자세한 내용은 자동 속성 초기화 도구 설명서를 참조하십시오.
읽기 전용 속성
선언
일반적인 오해, 특히 초보자는 읽기 전용 속성이 있습니다. readonly
키워드로 표시된 속성입니다. 그것은 정확하지 않고 실제로 다음은 컴파일 타임 오류입니다 .
public readonly string SomeProp { get; set; }
속성은 getter 만 있으면 읽기 전용입니다.
public string SomeProp { get; }
읽기 전용 속성을 사용하여 변경 불가능한 클래스 만들기
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;
}
}