เรียกใช้งาน property และ method แบบ dynamic ตอน run time ด้วย reflection ในภาษา C# Sharp และ Java

เนื่องจากภาษา C# และ Java เป็นภาษา Static Type (C# ตั้งแต่ version 4 มี dynamic ให้ใช้ แต่ไม่ขอกล่าวในที่นี้)

การจะใช้งาน property method ของ object ใดๆ เราก็ต้องเขียนไว้ตั้งแต่ตอน compile time

ตัวอย่างการใช้งาน C# property method แบบทั่วไป

สร้าง class user มี property FirstName, LastName และ method GetFullName

namespace CodeSanook.Examples.CSharp.Reflections
{
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public string GetFullName()
        {
            return $"{FirstName} {LastName}";
        }
    }
}

เมื่อจะเรียกใช้งาน class User ในตัวอย่างนี้ ผมเขียนอยู่ในรูปแบบของ XUnit unit test project

using CodeSanook.Examples.CSharp.Reflections;
using System.Reflection;
using Xunit;

namespace CodeSanook.Examples.CSharp.Tests.Reflections
{
    public class UserTests
    {
        [Fact]
        public void GetFullName_ValidPropertyValues_ReturnCorrectFullName()
        {
            var user = new User();
            user.FirstName = "Anthony";
            user.LastName = "CodeSanook";
            var fulllName = user.GetFullName();

            Assert.Equal("Anthony CodeSanook", fulllName);
        }
    }
}

จะเห็นได้ว่าการจะเรียกใช้งาน property หรือ method ได้เราต้องใช้งานผ่าน . syntax เท่านั้น ซึ่งหมายความว่าเราต้องรู้แน่นอนแล้วว่าเราจะเรียกใช้ property หรือ method ที่ชื่อว่าอะไร

สำหรับการใช้งานใน Java ก็เป็นแบบทำนองเดียวกันครับ แต่ใน Java เราจะใช้ getter setter method แทน

package com.codesanook.examples.reflection;

public class User {

    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return String.format("%s %s", this.firstName, this.lastName);
    }
}

การใช้งาน class User ในภาษา Java

package com.codesanook.examples.reflection.tests

import com.codesanook.examples.reflection.User
import spock.lang.Specification
import java.lang.reflect.*

class UserTests extends Specification {

    def "getFullName should return correct full name"() {
        given:
            def user =new User()
            user.setFirstName("Anthony")
            user.setLastName("CodeSanook")
        when:
            def result = user.getFullName()
        then:
            result == "Anthony CodeSanook"
    }

ทีนี้ ถ้าเราต้องการสร้าง program ให้เกิดความยืดหยุ่น เช่น เราต้องการ ที่จะเรียกใช้ property หรือ method ด้้วย string จะต้องทำอย่างไร นี่จึงเป็นที่มาของ reflection ในภาษา C# และ Java ที่ทำให้เราสามารถเรียกใช้ property หรือ method ตอน run time ได้เลย

อีกทั้งเราสามารถเข้าไปวน loop ดู property fields attribute etc ก็ได้

ตัวอย่างที่มีการนำไปใช้ เช่น

  • ORM ในการ map property เข้ากับ filed ของ database drop down list
  • ข้อมูลที่ให้ผู้ใช้เลือก property ของ object ได้เลย

code ตัวอย่างต่อไปจะแสดงการใช้งาน reflection แบบง่ายๆ ใน ภาษา C# และ Java ครับ

ตัวอย่างภาษา C#

[Fact]
public void GetFullNameWithReflection_ValidPropertyValues_ReturnCorrectFullName()
{
    var user = new User();
    var userType = user.GetType();
    var firstNameProperty = userType.GetProperty("FirstName");
    var lastNameProperty = userType.GetProperty("LastName");

    firstNameProperty.SetValue(user, "Anthony");
    lastNameProperty.SetValue(user, "CodeSanook");

    var getfullNameMethod = userType.GetMethod("GetFullName");
    var fullName = getfullNameMethod.Invoke(user, null);
    Assert.Equal("Anthony CodeSanook", fullName);
}

ตัวอย่างภาษา Java

def "invoke getFullName method with Reflection should return correct full name"() {
    given:
        def user = new User()
        user.setFirstName("Anthony")
        user.setLastName("CodeSanook")
    when:
        Class userClass = user.getClass()
        Method method = userClass.getMethod("getFullName")
        def fullName = method.invoke(user, null)
    then:
        fullName == "Anthony CodeSanook"
}

def "getDeclaredMethods with Reflection should return 5 method"() {
    given:
        def user = new User()
    when:
        Class userClass = user.getClass();
        Method[] methods = userClass.getDeclaredMethods();
    then:
        methods.length == 5
}

reflect ยังมี API ให้ใช้งาน และประยุกต์ใช้ได้อีกมากครับ แต่ในตัวอย่างนี้ ขอนำเสนอแบบเบื้องต้นเพื่อให้ผู้อ่านได้เห็นภาพ เพราะหากผู้อ่านได้เขียน program ไปสักพักจะพบว่าจะมีเหตุการณ์ให้เราสามารถนำส่วนนี้ไปประยุกต์ใช้ได้ รวมถึงเมื่อเข้าไปอ่าน source code opensource ต่างๆ เมื่อเห็นส่วนนี้ เราก็จะทำความเข้าใจได้ง่าย

แต่ reflection ก็ไม่ควรหากไม่จำเป็นนะครับ เพราะมีผลต่อ performance

source code ของ project นี้นะครับ

https://github.com/aaronamm/CodeSanook.Examples

ใน source code project นี้มีตัวอย่างที่น่าสนใจครับ ผมใช้ Travis CI ที่ build ทั้ง Java และ C# ร่วมกัน น่าจะมีประโยชน์สำหรับท่านที่ต้องการใช้ Travis เพื่อ support project แบบ multiple languages ครับผม

Travis CI build status

Build Status