1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VB.NET 4.8.1を今の時代に触る!!PHP 8.x, TypeScript 5.xとの言語仕様比較(第18章 リフレクションとメタプログラミング)

1
Last updated at Posted at 2025-12-27

目次
第17章 エラーハンドリング

第18章 リフレクションとメタプログラミング

18.1 型情報の取得

PHP

<?php
class User {
    public string $name;
    private int $age;

    public function __construct(string $name, int $age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    public function greet(): string
    {
        return "Hello, {$this->name}";
    }
}

// ReflectionClass
$reflection = new ReflectionClass(User::class);

// クラス情報
echo $reflection->getName();           // User
echo $reflection->getShortName();      // User
echo $reflection->isAbstract();        // false
echo $reflection->isFinal();           // false

// プロパティ
foreach ($reflection->getProperties() as $property) {
    echo $property->getName();         // name, age
    echo $property->getType();         // string, int
    echo $property->isPublic();        // true, false
}

// メソッド
foreach ($reflection->getMethods() as $method) {
    echo $method->getName();
    echo $method->getReturnType();

    // パラメータ
    foreach ($method->getParameters() as $param) {
        echo $param->getName();
        echo $param->getType();
    }
}

// インスタンスからの取得
$user = new User("John", 30);
$objReflection = new ReflectionObject($user);

TypeScript

// TypeScript の型情報は実行時に消える
// reflect-metadata ライブラリを使用

import "reflect-metadata";

class User {
    @Reflect.metadata("design:type", String)
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

// 実行時の型チェック
const user = new User("John");
console.log(typeof user);           // "object"
console.log(user instanceof User);  // true
console.log(user.constructor.name); // "User"

// Object.keys でプロパティ名を取得
console.log(Object.keys(user));     // ["name"]

// プロパティの存在確認
console.log("name" in user);        // true
console.log(user.hasOwnProperty("name")); // true

// プロトタイプチェーン
console.log(Object.getPrototypeOf(user)); // User.prototype

VB.NET

Class User
    Public Property Name As String
    Private Age As Integer

    Public Sub New(name As String, age As Integer)
        Me.Name = name
        Me.Age = age
    End Sub

    Public Function Greet() As String
        Return $"Hello, {Name}"
    End Function
End Class

' Type オブジェクトの取得
Dim type1 = GetType(User)
Dim type2 = (New User("John", 30)).GetType()
Dim type3 = Type.GetType("MyNamespace.User")

' 型情報
Console.WriteLine(type1.Name)          ' User
Console.WriteLine(type1.FullName)      ' MyNamespace.User
Console.WriteLine(type1.IsAbstract)    ' False
Console.WriteLine(type1.IsSealed)      ' False

' プロパティ
For Each prop In type1.GetProperties()
    Console.WriteLine(prop.Name)
    Console.WriteLine(prop.PropertyType)
    Console.WriteLine(prop.CanRead)
    Console.WriteLine(prop.CanWrite)
Next

' フィールド(Private含む)
For Each field In type1.GetFields(BindingFlags.NonPublic Or BindingFlags.Instance)
    Console.WriteLine(field.Name)
    Console.WriteLine(field.FieldType)
Next

' メソッド
For Each method In type1.GetMethods()
    Console.WriteLine(method.Name)
    Console.WriteLine(method.ReturnType)

    ' パラメータ
    For Each param In method.GetParameters()
        Console.WriteLine(param.Name)
        Console.WriteLine(param.ParameterType)
    Next
Next

18.2 動的メソッド呼び出し

PHP

<?php
$user = new User("John", 30);

// 可変関数/メソッド
$methodName = "greet";
echo $user->$methodName();  // Hello, John

// call_user_func
echo call_user_func([$user, 'greet']);
echo call_user_func_array([$user, 'setName'], ['Jane']);

// Reflection
$reflection = new ReflectionMethod(User::class, 'greet');
echo $reflection->invoke($user);

// プライベートメソッドの呼び出し
$reflection = new ReflectionMethod(User::class, 'privateMethod');
$reflection->setAccessible(true);
$reflection->invoke($user);

// プロパティへのアクセス
$property = new ReflectionProperty(User::class, 'age');
$property->setAccessible(true);
echo $property->getValue($user);
$property->setValue($user, 31);

TypeScript

const user = new User("John");

// ブラケット記法
const methodName = "greet";
(user as any)[methodName]();

// Reflect API
Reflect.get(user, "name");
Reflect.set(user, "name", "Jane");
Reflect.has(user, "name");

// apply
const method = user.greet;
Reflect.apply(method, user, []);

// Function.prototype.call/apply
user.greet.call(user);
user.greet.apply(user, []);

// Proxy
const handler: ProxyHandler<User> = {
    get(target, prop) {
        console.log(`Getting ${String(prop)}`);
        return Reflect.get(target, prop);
    },
    set(target, prop, value) {
        console.log(`Setting ${String(prop)} to ${value}`);
        return Reflect.set(target, prop, value);
    }
};

const proxy = new Proxy(user, handler);
proxy.name;  // ログ出力される

VB.NET

Dim user As New User("John", 30)

' CallByName(レガシー)
Dim result = CallByName(user, "Greet", CallType.Method)

' Reflection
Dim type = user.GetType()
Dim method = type.GetMethod("Greet")
Dim result2 = method.Invoke(user, Nothing)

' パラメータ付き
Dim setMethod = type.GetMethod("SetName")
setMethod.Invoke(user, {"Jane"})

' プライベートメソッド
Dim privateMethod = type.GetMethod("PrivateMethod",
    BindingFlags.NonPublic Or BindingFlags.Instance)
privateMethod.Invoke(user, Nothing)

' プロパティへのアクセス
Dim nameProp = type.GetProperty("Name")
Dim nameValue = nameProp.GetValue(user)
nameProp.SetValue(user, "Jane")

' フィールドへの直接アクセス
Dim ageField = type.GetField("Age", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim ageValue = ageField.GetValue(user)
ageField.SetValue(user, 31)

' Dynamic(Option Strict Off の場合)
Option Strict Off
Dim obj As Object = user
obj.Greet()  ' 実行時に解決

18.3 動的オブジェクト生成

PHP

<?php
// クラス名からインスタンス生成
$className = "User";
$user = new $className("John", 30);

// Reflection
$reflection = new ReflectionClass(User::class);
$user = $reflection->newInstance("John", 30);
$user = $reflection->newInstanceArgs(["John", 30]);

// パラメータなしコンストラクタ
$user = $reflection->newInstanceWithoutConstructor();

// 無名クラス
$obj = new class {
    public string $name = "Anonymous";
};

TypeScript

// コンストラクタ参照
const UserClass = User;
const user = new UserClass("John");

// ファクトリ関数
function createInstance<T>(
    ctor: new (...args: any[]) => T,
    ...args: any[]
): T {
    return new ctor(...args);
}

const user2 = createInstance(User, "John");

// Object.create
const userProto = Object.create(User.prototype);
User.call(userProto, "John");

// クラスのレジストリ
const classRegistry = new Map<string, new (...args: any[]) => any>();
classRegistry.set("User", User);

const ClassName = "User";
const UserCtor = classRegistry.get(ClassName);
if (UserCtor) {
    const user3 = new UserCtor("John");
}

VB.NET

' Activator
Dim user1 = Activator.CreateInstance(Of User)()
Dim user2 = DirectCast(Activator.CreateInstance(GetType(User)), User)

' パラメータ付き
Dim user3 = Activator.CreateInstance(GetType(User), {"John", 30})

' 型名から(文字列から)
Dim typeName = "MyNamespace.User, MyAssembly"
Dim type = Type.GetType(typeName)
Dim user4 = Activator.CreateInstance(type, {"John", 30})

' Reflection
Dim ctor = GetType(User).GetConstructor({GetType(String), GetType(Integer)})
Dim user5 = ctor.Invoke({"John", 30})

' ジェネリック型の生成
Dim listType = GetType(List(Of))
Dim genericType = listType.MakeGenericType(GetType(User))
Dim list = Activator.CreateInstance(genericType)

18.4 コード生成

PHP

<?php
// eval(非推奨、セキュリティリスク)
$code = 'return $a + $b;';
$result = eval($code);

// 動的関数生成
$fn = create_function('$a, $b', 'return $a + $b;');  // 非推奨

// クロージャで代替
$fn = function($a, $b) { return $a + $b; };

// include でコード読み込み
include 'generated_code.php';

TypeScript

// Function コンストラクタ(非推奨)
const fn = new Function('a', 'b', 'return a + b');
console.log(fn(1, 2));  // 3

// eval(非推奨)
const result = eval('1 + 2');

// 安全なコード生成:テンプレート
function generateClass(name: string, properties: string[]): string {
    return `
class ${name} {
    ${properties.map(p => `${p}: any;`).join('\n    ')}

    constructor(${properties.map(p => `${p}: any`).join(', ')}) {
        ${properties.map(p => `this.${p} = ${p};`).join('\n        ')}
    }
}`;
}

VB.NET

' Reflection.Emit(動的アセンブリ生成)
Dim assemblyName = New AssemblyName("DynamicAssembly")
Dim assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
    assemblyName, AssemblyBuilderAccess.Run)
Dim moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule")

' 動的型の定義
Dim typeBuilder = moduleBuilder.DefineType("DynamicUser",
    TypeAttributes.Public Or TypeAttributes.Class)

' プロパティの追加
Dim fieldBuilder = typeBuilder.DefineField("_name",
    GetType(String), FieldAttributes.Private)
Dim propertyBuilder = typeBuilder.DefineProperty("Name",
    PropertyAttributes.None, GetType(String), Nothing)

' ゲッターの定義
Dim getterBuilder = typeBuilder.DefineMethod("get_Name",
    MethodAttributes.Public Or MethodAttributes.SpecialName,
    GetType(String), Type.EmptyTypes)
Dim getterIL = getterBuilder.GetILGenerator()
getterIL.Emit(OpCodes.Ldarg_0)
getterIL.Emit(OpCodes.Ldfld, fieldBuilder)
getterIL.Emit(OpCodes.Ret)
propertyBuilder.SetGetMethod(getterBuilder)

' 型の作成
Dim dynamicType = typeBuilder.CreateType()
Dim instance = Activator.CreateInstance(dynamicType)

' Source Generators(コンパイル時コード生成、C#のみ)
' VB.NET では利用不可

18.5 メタプログラミングの実用例

依存性注入コンテナ

<?php
class Container {
    private array $bindings = [];

    public function bind(string $abstract, string $concrete): void {
        $this->bindings[$abstract] = $concrete;
    }

    public function resolve(string $abstract): object {
        $concrete = $this->bindings[$abstract] ?? $abstract;
        $reflection = new ReflectionClass($concrete);
        $constructor = $reflection->getConstructor();

        if (!$constructor) {
            return new $concrete();
        }

        $dependencies = [];
        foreach ($constructor->getParameters() as $param) {
            $type = $param->getType();
            if ($type && !$type->isBuiltin()) {
                $dependencies[] = $this->resolve($type->getName());
            }
        }

        return $reflection->newInstanceArgs($dependencies);
    }
}
' VB.NET
Public Class Container
    Private bindings As New Dictionary(Of Type, Type)

    Public Sub Bind(Of TAbstract, TConcrete As TAbstract)()
        bindings(GetType(TAbstract)) = GetType(TConcrete)
    End Sub

    Public Function Resolve(Of T)() As T
        Return DirectCast(Resolve(GetType(T)), T)
    End Function

    Private Function Resolve(type As Type) As Object
        Dim concrete As Type = Nothing
        If Not bindings.TryGetValue(type, concrete) Then
            concrete = type
        End If

        Dim ctor = concrete.GetConstructors().FirstOrDefault()
        If ctor Is Nothing Then
            Return Activator.CreateInstance(concrete)
        End If

        Dim dependencies = ctor.GetParameters().
            Select(Function(p) Resolve(p.ParameterType)).
            ToArray()

        Return ctor.Invoke(dependencies)
    End Function
End Class

自動シリアライズ

// TypeScript - デコレータベースのシリアライズ
const SERIALIZABLE_KEY = Symbol("serializable");

function Serializable(target: any, propertyKey: string) {
    const properties: string[] =
        Reflect.getMetadata(SERIALIZABLE_KEY, target) || [];
    properties.push(propertyKey);
    Reflect.defineMetadata(SERIALIZABLE_KEY, properties, target);
}

function serialize(obj: any): string {
    const properties: string[] =
        Reflect.getMetadata(SERIALIZABLE_KEY, obj) || [];
    const result: Record<string, any> = {};
    for (const prop of properties) {
        result[prop] = obj[prop];
    }
    return JSON.stringify(result);
}

class User {
    @Serializable name: string = "";
    @Serializable email: string = "";
    password: string = "";  // シリアライズされない
}
' VB.NET - 属性ベースのシリアライズ
<AttributeUsage(AttributeTargets.Property)>
Public Class SerializableAttribute
    Inherits Attribute
End Class

Public Class Serializer
    Public Shared Function Serialize(obj As Object) As String
        Dim type = obj.GetType()
        Dim result As New Dictionary(Of String, Object)

        For Each prop In type.GetProperties()
            If prop.GetCustomAttribute(Of SerializableAttribute)() IsNot Nothing Then
                result(prop.Name) = prop.GetValue(obj)
            End If
        Next

        Return JsonConvert.SerializeObject(result)
    End Function
End Class

Public Class User
    <Serializable>
    Public Property Name As String

    <Serializable>
    Public Property Email As String

    Public Property Password As String  ' シリアライズされない
End Class

付録

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?