type : string, int, float, double 등등... 변수와 상수가 갖는 type이다. 함수는 type 형식의 parameters를 전달받고 values를 반환할 수 있다. type에는 여러 카테고리가 있는데, built-in value types, built-in reference types가 대부분이다.
System.Type : type에 상관없이 object, variables, constants에 reflect할 수 있는 함수를 제공한다.
Reflection : OOP에서 우리는 class, struct 와 같은 model을 사용하여 object의 성격과 행동을 정의한다. 이러한 model을 설명하는 data를 metadata라고 한다. runtime에서 Reflection이라 불리는 내장 매커니즘을 통하여 이러한 metadata를 다룰 수 있다. C# Reflection의 생태계는 주로 System.Type class와 System.Reflection namespace의 class/interfaces로 이루어져있다. 이러한 생태계를 사용하여 objects의 type을 조사할 수 있다. 이러한 능력은 아래와 같은 runtime 확장력을 가진다.
- Look for certain characteristics in target objects and behave accordingly
- Dynamic instantiation of objects from type information
- Late binding of fields, properties, and methods
- Copy data from one object to another
- Collection of application insights and take pre-emptive measurements like Dependency Injection for example
- Design of complex routines which need access to a part of code that is rather inaccessible in compile time
- Creating new types at runtime on demand
Type 정보 추출하기
public interface IMotionSensor
{
void Observe();
void Observe(string direction);
}
[Description("Detects movements in the vicinity")]
public class MotionSensor : IMotionSensor
{
private int _detections;
public string? FocalPoint;
public MotionSensor() : this("*") { }
public MotionSensor(string focalPoint) => FocalPoint = focalPoint;
public event EventHandler<string>? MotionDetected;
[Description("Turn On/Off")]
public bool Enabled { get; set; }
public string Status => _detections > 0 ? "Red" : "Green";
public bool IsCritical(int threshold) => _detections >= threshold;
public void Observe() => RaiseMotionDetected("*");
public void Observe(string direction) => RaiseMotionDetected(direction);
private void RaiseMotionDetected(string direction)
{
_detections++;
FocalPoint = direction;
MotionDetected?.Invoke(this, direction);
}
}
3가지 방법으로 type을 추출해보자
var type1 = typeof(MotionSensor);
Console.WriteLine(type1); //ConsoleApp1.MotionSensor
var sensor = new MotionSensor();
var type2 = sensor.GetType();
Console.WriteLine(type2);//ConsoleApp1.MotionSensor
Console.WriteLine(type2.AssemblyQualifiedName);//ConsoleApp1.MotionSensor, ConsoleApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
var type3 = Type.GetType(type2.AssemblyQualifiedName);
Console.WriteLine(type3); //ConsoleApp1.MotionSensor
System.Type의 property를 사용하여 MotionSensor의 metadata를 살펴보자.
var type1 = typeof(MotionSensor);
Console.WriteLine(type1.Name);//MotionSensor
Console.WriteLine(type1.Namespace);//ConsoleApp1
Console.WriteLine(type1.FullName);//ConsoleApp1.MotionSensor
Console.WriteLine(type1.AssemblyQualifiedName);//ConsoleApp1.MotionSensor, ConsoleApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Console.WriteLine(type1.IsClass);//True
Console.WriteLine(type1.IsValueType);//False
object에서 System.Type가져오기
object에서 System.Type을 가져오려면 GetType()함수를 호출하면 된다.
namespace ConsoleApp1
{
class Demo
{
static void Main(string[] args)
{
Demo demo = new(); // Create new instance of the object
Type demoType = demo.GetType(); // Get the type of the object
Console.WriteLine(demoType); // Print it our to the console
// ConsoleApp1.Demo
}
}
}
var type의 변수를 정의하고, 컴파일 타임에 추론된 type을 가져올때 System.Type을 사용할 수 있다.
var x = 26;
Console.WriteLine(x.GetType());
// System.Int32
type에서 System.Type가져오기
typeof를 활용할 수 있다.
namespace ConsoleApp1
{
class Demo
{
static void Main(string[] args)
{
Type demoType = typeof(Demo);
Console.WriteLine(demoType);
// ConsoleApp1.Demo
}
}
}
GetType과 typeof의 차이점
1. .GetType()은 run time에서 실행된다, typeof는 compile time에 실행된다.
2. .GetType()은 object instance의 System.Type을 얻기 위해서만 사용하고, typeof는 type의 System.Type을 return한다.
동적 객체 생성
var type = typeof(MotionSensor);
var sensor1 = Activator.CreateInstance(type)!;
var sensor2 = Activator.CreateInstance(type, new[] { "left" })!;
if (sensor1 is MotionSensor)
Console.WriteLine(((MotionSensor)sensor1).FocalPoint); //*
if (sensor2 is MotionSensor)
Console.WriteLine(((MotionSensor)sensor2).FocalPoint);//left
System.Activator class로 MotionSensor type으로 MotionSensor instance를 만들 수 있다. parameter가 없는 constructor는 CreateInstance method에 type을 전달하여 실행하고, parameter가 있는 constructor는 array형태의 arguments가 필요하다.
private constructor를 사용해야 할 때도 있다.
public class InternalTracker
{
private static int _instanceCount = 0;
private InternalTracker() => Sequence = ++_instanceCount;
public int Sequence { get; }
}
class Demo
{
static void Main(string[] args)
{
var type = typeof(InternalTracker);
var tracker = Activator.CreateInstance(type, nonPublic: true)!;
}
}
assemblies, arguments, attributes와 함께 reflection을 사용할 수 있는 방법을 살펴보자.
assemblies로부터 정보 얻기
static void Main(string[] args)
{
// Get a Type object.
Type t = typeof(int);
// Instantiate an Assembly class to the assembly housing the Integer type.
Assembly assem = Assembly.GetAssembly(t);
// Display the name of the assembly.
Console.WriteLine("Name: {0}", assem.FullName);
// Get the location of the assembly using the file: protocol.
Console.WriteLine("CodeBase: {0}", assem.CodeBase);
}
//Name: System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
//CodeBase: file:///C:/Program Files/dotnet/shared/Microsoft.NETCore.App/6.0.11/System.Private.CoreLib.dll
위의 방법은 Type.Assembly property를 가져오는 것과 동일하다. Type.Assembly를 사용하는 것이 더 빠르다.
static void Main(string[] args)
{
Console.WriteLine(typeof(int).Assembly.FullName);
Console.WriteLine(typeof(int).Assembly.CodeBase);
}
object의 실제 Type을 확인하는 방법
거대한 튜플을 사용하는 대신, variables를 반환할 수 있는 generic/dynamic한 방법은 object-typed variables를 사용하는 것이다. 하지만 여기에는 문제점이 있는데, methods, variables, properties에 접근할 수 없다.
public class Dog
{
public Dog(string name)
{
Name = name;
}
public string Name { get; }
public void Bark()
{
Console.WriteLine($"Wuf wuf, I am {Name}! Can I have a treat?");
}
}
class Demo
{
static void Main(string[] args)
{
object kenya = new Dog("Kenya");
Console.WriteLine(kenya.Name);
}
}
컴파일러 에러가 발생한다.
object-typed variable을 type으로 cast한다. reflection이 있기 떄문에, System.Type object를 가져와서 원하는 정보를 얻을 수 있다.
class Demo
{
static void Main(string[] args)
{
//1.Kenya이름의 type Dog의 object instance를 생성한다.
object kenya = new Dog("Kenya"); // Kenya is my dog
Type kenyaType = kenya.GetType();
// 2. Get the property and method info from the Type "kenyaType"
PropertyInfo kenyaNameProperty = kenyaType.GetProperty("Name");
MethodInfo methodInfoProperty = kenyaType.GetMethod("Bark");
// 3. Retrieve the value or invoke the method on an instance
Console.WriteLine($"From {kenyaNameProperty?.GetValue(kenya)}");
methodInfoProperty?.Invoke(kenya, new object[] { });
}
}
//output
//From Kenya
//Wuf wuf, I am Kenya! Can I have a treat?
object's Type Arguments를 확인하는 방법
Dictionary<string, double> dogs = new() { { "Kenya", 1.5 } };
Type dogTypes = dogs.GetType();
Type[] typeArguments = dogTypes.GetGenericArguments(); // Store the type arguments in an array
// print each type argument in the console
foreach (Type typeParameter in typeArguments)
{
Console.WriteLine($"Type Argument is of Type: {typeParameter}");
}
//output
//Type Argument is of Type: System.String
//Type Argument is of Type: System.Double
dog dictionary에 제공된 Type arguments를 얻기 위해 GetGenericArguments()를 호출할 수 있다.
object의 모든 attributes 보여주기
type에 GetCustomAttributes() 함수만 사용하면 된다.
public class OriginCountryAttribute : Attribute
{
public string originCountry;
public OriginCountryAttribute(string originCountry)
{
this.originCountry = originCountry;
}
}
[OriginCountry("French")]
public class Dog
{
public Dog(string name)
{
Name = name;
}
public string Name { get; }
public void Bark()
{
Console.WriteLine($"Wuf wuf, I am {Name}! Can I have a treat?");
}
}
class Demo
{
static void Main(string[] args)
{
Type dogType = typeof(Dog);
foreach (Attribute attr in Attribute.GetCustomAttributes(dogType))
{
Console.WriteLine($"Attribute: {attr}");
//Attribute: ConsoleApp1.OriginCountryAttribute
}
}
}
'C#' 카테고리의 다른 글
C#] Method Parameters - params, out (0) | 2023.08.23 |
---|---|
C#] Method Parameters - parameters 전달 (0) | 2023.08.22 |
C#] System.Collections.Immutable (0) | 2023.05.18 |
C#] Asynchronous VS Multithreading (0) | 2023.04.25 |
C#] 특수문자 (주석, $, @) (0) | 2023.03.21 |
댓글