본문 바로가기
C#

C#] type, System.Type, System.Reflection

by Fastlane 2023. 6. 19.
728x90
반응형

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
        }
    }
}

 

728x90
반응형

'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

댓글