Properties combine aspects of both fields and methods. To the user of an object, a property appears to be a field; accessing the property requires the same syntax. To the implementer of a class, a property is one or two code blocks, representing a get accessor and/or a set or init accessor. The code block for the get accessor is executed when the property is read; the code block for the set or init accessor is executed when the property is assigned a value. A property without a set accessor is considered read-only. A property without a get accessor is considered write-only. A property that has both accessors is read-write. You can use an init accessor instead of a set accessor to enable the property to be set as part of object initialization but otherwise make it read-only.
Unlike fields, properties aren't classified as variables. Therefore, you can't pass a property as a ref or out parameter.
Properties have many uses:
- They can validate data before allowing a change.
- They can transparently expose data on a class where that data is retrieved from some other source, such as a database.
- They can take an action when data is changed, such as raising an event, or changing the value of other fields.
Properties are declared in the class block by specifying the access level of the field, followed by the type of the property, followed by the name of the property, and followed by a code block that declares a get-accessor and/or a set accessor. For example:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.csUsingExample
namespace properties;
//<UsingExample>
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
//</UsingExample>
//<FieldExample>
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
//</FieldExample>In this example, Month is declared as a property so that the set accessor can make sure that the Month value is set between 1 and 12. The Month property uses a private field to track the actual value. The real location of a property's data is often referred to as the property's "backing store." It's common for properties to use private fields as a backing store. The field is marked private in order to make sure that it can only be changed by calling the property. For more information about public and private access restrictions, see Access Modifiers. Automatically implemented properties provide simplified syntax for simple property declarations. For more information, see Automatically implemented properties.
Beginning with C# 14, you can use field backed properties to add validation to the set accessor of an automatically implemented property, as shown in the following example:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/TimePeriod.csFieldExample
namespace properties;
//<UsingExample>
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
//</UsingExample>
//<FieldExample>
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
//</FieldExample>The get accessor
The body of the get accessor resembles that of a method. It must return a value of the property type. The C# compiler and Just-in-time (JIT) compiler detect common patterns for implementing the get accessor, and optimizes those patterns. For example, a get accessor that returns a field without performing any computation is likely optimized to a memory read of that field. Automatically implemented properties follow this pattern and benefit from these optimizations. However, a virtual get accessor method can't be inlined because the compiler doesn't know at compile time which method might actually be called at run time. The following example shows a get accessor that returns the value of a private field _name:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.csUsingEmployeeExample
using System.Diagnostics.CodeAnalysis;
namespace VersionOne
{
// <Field>
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
// </Field>
}
namespace VersionTwo
{
// <AutoImplemented>
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
// </AutoImplemented>
}
namespace VersionTwoPointFive
{
// <FieldBackedProperty>
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
// </FieldBackedProperty>
}
namespace VersionThree
{
// <Initializer>
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
// </Initializer>
}
namespace VersionFour
{
// <AccessorModifiers>
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
// </AccessorModifiers>
}
namespace VersionFive
{
// <Readonly>
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
// </Readonly>
}
namespace VersionSix
{
// <InitOnly>
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
// </InitOnly>
}
namespace VersionSeven
{
// <Required>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
// </Required>
}
namespace VersionEight
{
// <ExpressionBodiedProperty>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
// </ExpressionBodiedProperty>
}
namespace VersionNine
{
// <CachedBackingStore>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </CachedBackingStore>
}
namespace VersionTen
{
// <UseBackingFields>
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </UseBackingFields>
}
namespace properties
{
// <UsingEmployeeExample>
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
// </UsingEmployeeExample>
// <ManageExample>
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
// </ManageExample>
//<StudentExample>
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
//</StudentExample>
}When you reference the property, except as the target of an assignment, the get accessor is invoked to read the value of the property. For example:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/Program.csGetAccessor
// See https://aka.ms/new-console-template for more information
using properties;
using VersionSeven;
//<GetAccessor>
var employee = new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
//</GetAccessor>
//<SetAccessor>
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
//</SetAccessor>
HidingExample.TestHiding.Test();
// <SnippetInitialize>
var aPerson = new Person("John");
aPerson = new Person { FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
// </SnippetInitialize>The get accessor must be an expression-bodied member, or end in a return or throw statement, and control can't flow off the accessor body.
The get accessor can be used to return the field value or to compute it and return it. For example:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.csManageExample
using System.Diagnostics.CodeAnalysis;
namespace VersionOne
{
// <Field>
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
// </Field>
}
namespace VersionTwo
{
// <AutoImplemented>
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
// </AutoImplemented>
}
namespace VersionTwoPointFive
{
// <FieldBackedProperty>
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
// </FieldBackedProperty>
}
namespace VersionThree
{
// <Initializer>
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
// </Initializer>
}
namespace VersionFour
{
// <AccessorModifiers>
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
// </AccessorModifiers>
}
namespace VersionFive
{
// <Readonly>
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
// </Readonly>
}
namespace VersionSix
{
// <InitOnly>
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
// </InitOnly>
}
namespace VersionSeven
{
// <Required>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
// </Required>
}
namespace VersionEight
{
// <ExpressionBodiedProperty>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
// </ExpressionBodiedProperty>
}
namespace VersionNine
{
// <CachedBackingStore>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </CachedBackingStore>
}
namespace VersionTen
{
// <UseBackingFields>
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </UseBackingFields>
}
namespace properties
{
// <UsingEmployeeExample>
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
// </UsingEmployeeExample>
// <ManageExample>
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
// </ManageExample>
//<StudentExample>
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
//</StudentExample>
}In the previous example, if you don't assign a value to the Name property, it returns the value NA.
The set accessor
The set accessor resembles a method whose return type is void. It uses an implicit parameter called value, whose type is the type of the property. The compiler and JIT compiler also recognize common patterns for a set or init accessor. Those common patterns are optimized, directly writing the memory for the backing field. In the following example, a set accessor is added to the Name property:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/Person.csStudentExample
using System.Diagnostics.CodeAnalysis;
namespace VersionOne
{
// <Field>
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
// </Field>
}
namespace VersionTwo
{
// <AutoImplemented>
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
// </AutoImplemented>
}
namespace VersionTwoPointFive
{
// <FieldBackedProperty>
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
// </FieldBackedProperty>
}
namespace VersionThree
{
// <Initializer>
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
// </Initializer>
}
namespace VersionFour
{
// <AccessorModifiers>
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
// </AccessorModifiers>
}
namespace VersionFive
{
// <Readonly>
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
// </Readonly>
}
namespace VersionSix
{
// <InitOnly>
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
// </InitOnly>
}
namespace VersionSeven
{
// <Required>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
// </Required>
}
namespace VersionEight
{
// <ExpressionBodiedProperty>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
// </ExpressionBodiedProperty>
}
namespace VersionNine
{
// <CachedBackingStore>
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </CachedBackingStore>
}
namespace VersionTen
{
// <UseBackingFields>
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
// </UseBackingFields>
}
namespace properties
{
// <UsingEmployeeExample>
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
// </UsingEmployeeExample>
// <ManageExample>
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
// </ManageExample>
//<StudentExample>
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
//</StudentExample>
}When you assign a value to the property, the set accessor is invoked by using an argument that provides the new value. For example:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/properties/Program.csSetAccessor
// See https://aka.ms/new-console-template for more information
using properties;
using VersionSeven;
//<GetAccessor>
var employee = new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
//</GetAccessor>
//<SetAccessor>
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
//</SetAccessor>
HidingExample.TestHiding.Test();
// <SnippetInitialize>
var aPerson = new Person("John");
aPerson = new Person { FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
// </SnippetInitialize>It's an error to use the implicit parameter name, value, for a local variable declaration in a set accessor.
The init accessor
The code to create an init accessor is the same as the code to create a set accessor except that you use the init keyword instead of set. The difference is that the init accessor can only be used in the constructor or by using an object-initializer.
Remarks
Properties can be marked as public, private, protected, internal, protected internal, or private protected. These access modifiers define how users of the class can access the property. The get and set accessors for the same property can have different access modifiers. For example, the get might be public to allow read-only access from outside the type, and the set can be private or protected. For more information, see Access Modifiers.
A property can be declared as a static property by using the static keyword. Static properties are available to callers at any time, even if no instance of the class exists. For more information, see Static Classes and Static Class Members.
A property can be marked as a virtual property by using the virtual keyword. Virtual properties enable derived classes to override the property behavior by using the override keyword. For more information about these options, see Inheritance.
A property overriding a virtual property can also be sealed, specifying that for derived classes it's no longer virtual. Lastly, a property can be declared abstract. Abstract properties don't define an implementation in the class, and derived classes must write their own implementation. For more information about these options, see Abstract and Sealed Classes and Class Members.
Examples
This example demonstrates instance, static, and read-only properties. It accepts the name of the employee from the keyboard, increments NumberOfEmployees by 1, and displays the Employee name and number.
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/Properties/CompleteEmployee.csEmployeeExample
namespace EmployeeExample;
// <EmployeeExample>
public class Employee
{
public static int NumberOfEmployees;
private static int _counter;
private string _name;
// A read-write instance property:
public string Name
{
get => _name;
set => _name = value;
}
// A read-only static property:
public static int Counter => _counter;
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}
// </EmployeeExample>Hidden property example
This example demonstrates how to access a property in a base class that's hidden by another property that has the same name in a derived class:
sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/Properties/HidingProperty.csHiding
namespace HidingExample;
//<Hiding>
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Manager : Employee
{
private string _name;
// Notice the use of the new modifier:
//<NewProperty>
public new string Name
//</NewProperty>
{
get => _name;
set => _name = value + ", Manager";
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
// Derived class property.
m1.Name = "John";
// Base class property.
//<CastToBase>
((Employee)m1).Name = "Mary";
//</CastToBase>
System.Console.WriteLine($"Name in the derived class is: {m1.Name}");
System.Console.WriteLine($"Name in the base class is: {((Employee)m1).Name}");
}
}
/* Output:
Name in the derived class is: John, Manager
Name in the base class is: Mary
*/
//</Hiding>The following are important points in the previous example:
- The property
Namein the derived class hides the propertyNamein the base class. In such a case, thenewmodifier is used in the declaration of the property in the derived class:sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/Properties/HidingProperty.csNewProperty
namespace HidingExample; //<Hiding> public class Employee { private string _name; public string Name { get => _name; set => _name = value; } } public class Manager : Employee { private string _name; // Notice the use of the new modifier: //<NewProperty> public new string Name //</NewProperty> { get => _name; set => _name = value + ", Manager"; } } class TestHiding { public static void Test() { Manager m1 = new Manager(); // Derived class property. m1.Name = "John"; // Base class property. //<CastToBase> ((Employee)m1).Name = "Mary"; //</CastToBase> System.Console.WriteLine($"Name in the derived class is: {m1.Name}"); System.Console.WriteLine($"Name in the base class is: {((Employee)m1).Name}"); } } /* Output: Name in the derived class is: John, Manager Name in the base class is: Mary */ //</Hiding> - The cast
(Employee)is used to access the hidden property in the base class:sample · csharpdocs/csharp/programming-guide/classes-and-structs/snippets/Properties/HidingProperty.csCastToBase
namespace HidingExample; //<Hiding> public class Employee { private string _name; public string Name { get => _name; set => _name = value; } } public class Manager : Employee { private string _name; // Notice the use of the new modifier: //<NewProperty> public new string Name //</NewProperty> { get => _name; set => _name = value + ", Manager"; } } class TestHiding { public static void Test() { Manager m1 = new Manager(); // Derived class property. m1.Name = "John"; // Base class property. //<CastToBase> ((Employee)m1).Name = "Mary"; //</CastToBase> System.Console.WriteLine($"Name in the derived class is: {m1.Name}"); System.Console.WriteLine($"Name in the base class is: {((Employee)m1).Name}"); } } /* Output: Name in the derived class is: John, Manager Name in the base class is: Mary */ //</Hiding>
For more information about hiding members, see the new Modifier.
Override property example
In this example, two classes, Cube and Square, implement an abstract class, Shape, and override its abstract Area property. Note the use of the override modifier on the properties. The program accepts the side as an input and calculates the areas for the square and cube. It also accepts the area as an input and calculates the corresponding side for the square and cube.
sample · csharpsamples/snippets/csharp/VS_Snippets_VBCSharp/csProgGuideProperties/CS/Properties.csSnippet6
namespace CsCsrefProgrammingProperties
{
using System.Linq;
using System.Collections.Generic;
using System;
using System.Collections.ObjectModel;
//---------------------------------------------------------------------------
namespace WrapProperties
{
//<Snippet1>
class TimePeriod
{
private double seconds;
public double Hours
{
get => seconds / 3600;
set => seconds = value * 3600;
}
}
class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();
// Assigning the Hours property causes the 'set' accessor to be called.
t.Hours = 24;
// Evaluating the Hours property causes the 'get' accessor to be called.
System.Console.WriteLine("Time in hours: " + t.Hours);
}
}
// Output: Time in hours: 24
//</Snippet1>
}
//---------------------------------------------------------------------------
namespace WrapUsingExample3
{
//<Snippet6>
abstract class Shape
{
public abstract double Area
{
get;
set;
}
}
class Square : Shape
{
public double side;
//constructor
public Square(double s) => side = s;
public override double Area
{
get => side * side;
set => side = System.Math.Sqrt(value);
}
}
class Cube : Shape
{
public double side;
//constructor
public Cube(double s) => side = s;
public override double Area
{
get => 6 * side * side;
set => side = System.Math.Sqrt(value / 6);
}
}
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s = new Square(side);
Cube c = new Cube(side);
// Display the results:
System.Console.WriteLine($"Area of the square = {s.Area:F2}");
System.Console.WriteLine($"Area of the cube = {c.Area:F2}");
System.Console.WriteLine();
// Input the area:
System.Console.Write("Enter the area: ");
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine($"Side of the square = {s.side:F2}");
System.Console.WriteLine($"Side of the cube = {c.side:F2}");
}
}
/* Example Output:
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
*/
//</Snippet6>
}
class ReadOnlyPropertyExamples
{
private List<string> names;
public ReadOnlyPropertyExamples()
{
names = new List<string>() { "Deborah", "Alex", "Sasha" };
}
public IEnumerable<string> Names
{
get
{
return names;
}
}
public IEnumerable<string> Names_2
{
get
{
return from name in names select name;
}
}
public ReadOnlyCollection<string> Names_3
{
get
{
return names.AsReadOnly(); //return list as a ReadOnlyCollection
}
}
public List<string> BadProp
{
get { return names; }
}
}
class Program
{
static void Main()
{
ReadOnlyPropertyExamples ro = new ReadOnlyPropertyExamples();
System.Console.WriteLine("return names");
foreach(string s in ro.Names)
System.Console.WriteLine(s);
List<string> list = ro.Names.ToList();
list.Add("Joy");
Console.WriteLine("added 'Joy' to local list");
foreach(string s in ro.Names)
Console.WriteLine(s);
//names_2
System.Console.WriteLine("return from names...");
// foreach (string s in ro.Names_2)
// System.Console.WriteLine(s);
//list = ro.Names_2.ToList();
// list.Add("Joy");
// Console.WriteLine("added 'Joy' to local list");
var q = from s in ro.Names_2
where s.Contains('x')
select s;
foreach (string str in q)
Console.WriteLine(str);
List<string> list2 = ro.Names_2.ToList();
list2.RemoveAt(0);
foreach(string s in list2)
Console.WriteLine(s);
//names_3
System.Console.WriteLine("return AsReadOnly...");
foreach (string s in ro.Names_3)
System.Console.WriteLine(s);
//ROC allows [] access
Console.WriteLine($"names_3[0] = {ro.Names_3[0]}");
//throws runtime exceptions on unimplemented interface methods
IList<string> myList = (IList<string>)ro.Names_3;
try
{
myList.RemoveAt(0); //throws not supported exception at run time
}
catch (NotSupportedException e)
{
Console.WriteLine("RemoveAt is not implemented in ReadOnlyCollection<T>");
}
//add name then iterate private list
List<string> badList = ro.BadProp;
badList.Add("Ha ha");
foreach(string s in ro.Names)
Console.WriteLine(s);
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}