Main Content

Use MATLAB Handle Classes in C#

R2026b

Overview

The MATLAB® engine API for .NET supports MATLAB handle classes when using the strongly typed interface. This feature translates the behavior of MATLAB classes that inherit from handle classes into equivalent C# code, preserving specific features and behaviors. (since R2024a)

Key Features

When you generate a C# file from a MATLAB class that inherits from a handle class using the matlab.engine.typedinterface.generateCSharp function, you get this functionality:

  • Copy behavior: The generated C# code replicates MATLAB handle class copy behavior. In MATLAB, handle objects are reference types, meaning that when you copy these objects, both the original and the new variable refer to the same object.

  • Comparison operators: The C# representation of MATLAB handle classes supports comparison operations. You can compare C# objects, derived from MATLAB handle classes, using the standard operators ==, !=, <, >, <=, and >=.

  • isvalid function support: The C# interface supports the isvalid function, which checks if handle objects are valid or have been deleted.

  • delete function support: The C# interface supports the delete function.

Inherent Capabilities of MATLAB Handle Classes

Deriving from the MATLAB handle class enables a subclass to:

  • Inherit methods.

  • Define events and listeners.

  • Define dynamic properties.

  • Implement set and get methods.

  • Customize copy behavior.

Example Files

These example files demonstrate the use and integration of MATLAB handle classes with C# applications using the engine API for .NET:

  • BankAccount.m: This MATLAB class file inherits from the handle class and provides basic banking functionality. For more information, see MATLAB BankAccount Class.

  • generateBankAccount.m: A MATLAB script used to generate the corresponding C# file from the BankAccount class, illustrating the integration with the engine API for .NET.

    matlab.engine.typedinterface.generateCSharp( ...
        "BankAccount", ...
        Classes="BankAccount")
    
  • run_BankAccount.m: An example using the BankAccount class.

    %% Create a bank account with an initial balance of 100
    account = BankAccount(100);
    %% Deposit 50 into the account
    account.deposit(50);
    disp(['Balance after deposit: ',num2str(account.checkBalance())]);
    %% Withdraw 30 from the account
    account.withdraw(30);
    disp(['Balance after withdrawal: ',num2str(account.checkBalance())]);
    %% Create a joint account that references the same existing account
    jointAccount = account;
    %% Deposit 20 using the shared reference
    jointAccount.deposit(20);
    disp(['Balance from sharedAccount: ',num2str(jointAccount.checkBalance())]);
    disp(['Balance from original account: ',num2str(account.checkBalance())]);
    
  • Program.cs: This C# console application demonstrates the use of the generated C# file from the MATLAB BankAccount class. It replicates the functionality of the MATLAB script run_BankAccount.m, but within a .NET environment. This application shows key operations such as account creation, deposits, withdrawals, and balance inquiries, mirroring the actions performed in the MATLAB script.

    using System;
    using MathWorks.MATLAB.Engine;
    using MathWorks.MATLAB.Types;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Start MATLAB engine
            using (dynamic matlab = MATLABEngine.StartMATLAB())
            {
                //Set the current folder in MATLAB to the folder containing the MATLAB class definition
                matlab.cd(new RunOptions(nargout: 0), @"P:\MATLAB\work\handle_class_support\dotnet");
                
                // Create a new bank account with an initial balance of 100
                BankAccount account = new BankAccount(matlab, 100);
    
                // Deposit 50 into the account
                account.deposit(50);
                DisplayBalance(account, "Balance after deposit:");
    
                // Withdraw 30 from the account
                account.withdraw(30);
                DisplayBalance(account, "Balance after withdrawal:");
    
                // Create a joint account that references the same existing account
                BankAccount jointAccount = account;
    
                // Deposit 20 using the shared reference
                jointAccount.deposit(20);
                DisplayBalance(jointAccount, "Balance from sharedAccount:");
                DisplayBalance(account, "Balance from original account:");
            }
        }
    
        static void DisplayBalance(BankAccount account, string message)
        {
            double balance;
            account.checkBalance(out balance);
            Console.WriteLine($"{message} {balance}");
        }
    }
    

MATLAB BankAccount Class

classdef BankAccount < handle
    properties (Access = private)
        Balance (1,1) double {mustBeReal}
    end
    
    methods
        % Constructor to initialize the account with a balance
        function obj = BankAccount(initialBalance)
            arguments (Input)
                initialBalance (1,1) double {mustBeReal}
            end
            if nargin == 0
                initialBalance = 0;
            end
            obj.Balance = initialBalance;
        end
        
        % Method to deposit money
        function deposit(obj, amount)
            arguments (Input)
                obj (1,1) BankAccount
                amount (1,1) double {mustBeReal}
            end
            if amount > 0
                obj.Balance = obj.Balance + amount;
            else
                error('Amount must be positive');
            end
        end
        
        % Method to withdraw money
        function withdraw(obj, amount)
            arguments (Input)
                obj (1,1) BankAccount
                amount (1,1) double {mustBeReal}
            end
            if amount <= obj.Balance && amount > 0
                obj.Balance = obj.Balance - amount;
            else
                error('Insufficient funds or invalid amount');
            end
        end
        
        % Method to check the balance
        function bal = checkBalance(obj)
            arguments (Input)
                obj (1,1) BankAccount
            end
            arguments (Output)
                bal (1,1) double {mustBeReal}
            end
            bal = obj.Balance;
        end
    end
end

Class definition: The MATLAB class BankAccount is defined as a subclass of the handle class, which allows it to exhibit reference behavior. This means instances of this class can be passed by reference.

Private properties: The class has one private property, Balance, which is a double.

Methods: The class includes methods for depositing, withdrawing, and checking the balance. These methods ensure controlled access and modification of the Balance property.

Generated C# BankAccount File

/* File: BankAccount.cs
*
* MATLAB Strongly Typed Interface Version: R2024b
* C# source code generated on: 11-Jan-2024
*/
using System;
using MathWorks.MATLAB.Types;
using MathWorks.MATLAB.Exceptions;

[MATLABClass("BankAccount")]
public  class BankAccount:IEquatable<BankAccount> { 
    private dynamic _objrep;
    private dynamic _matlab;
    public  BankAccount(MATLABProvider _matlab, double initialBalance){
        this._matlab = _matlab;
        _objrep = (MATLABArray)this._matlab.BankAccount(new RunOptions(nargout:1),initialBalance);
    }
    private BankAccount (){}

    public void checkBalance(){
        _objrep.checkBalance(new RunOptions(nargout:0));
    }
    public void checkBalance( out double bal){
        bal = (double)_objrep.checkBalance(new RunOptions(nargout:1));
    }
    public void withdraw(double amount){
        _objrep.withdraw(new RunOptions(nargout:0),amount);
    }
    public void deposit(double amount){
        _objrep.deposit(new RunOptions(nargout:0),amount);
    }
    public void eq(dynamic B){
        _objrep.eq(new RunOptions(nargout:0),B);
    }
    public void eq(dynamic B,  out dynamic TF){
        TF = (MATLABArray)_objrep.eq(new RunOptions(nargout:1),B);
    }
    public void ne(dynamic B){
        _objrep.ne(new RunOptions(nargout:0),B);
    }
    public void ne(dynamic B,  out dynamic TF){
        TF = (MATLABArray)_objrep.ne(new RunOptions(nargout:1),B);
    }
    public static bool operator < (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.lt(obj1,obj2);
        return ret;
    }

    public static bool operator > (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.gt(obj1,obj2);
        return ret;
    }

    public static bool operator <= (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.le(obj1,obj2);
        return ret;
    }

    public static bool operator >= (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.ge(obj1,obj2);
        return ret;
    }

    public void delete(){
        _objrep.delete(new RunOptions(nargout:0));
    }
    public void isvalid(){
        _objrep.isvalid(new RunOptions(nargout:0));
    }
    public void isvalid( out dynamic validity){
        validity = (MATLABArray)_objrep.isvalid(new RunOptions(nargout:1));
    }
    public static bool operator == (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.eq(obj1,obj2);
        return ret;
    }

    public static bool operator != (BankAccount obj1, BankAccount obj2){
        bool ret = obj1._matlab.ne(obj1,obj2);
        return ret;
    }

    public override bool Equals (Object obj){
        if (obj == null){
            return false;
        }

        try{

            BankAccount _obj = (BankAccount)obj;
            return (this == _obj);
        }

        catch(Exception){

            return false;
        }

    }

    public bool Equals(BankAccount obj){
        return this == obj;
    }

    public override int GetHashCode(){
        UInt64 hashcode = _matlab.keyHash(_objrep);
        return hashcode.GetHashCode();
    }

    public static implicit operator MATLABObject(BankAccount bankaccount){
        return bankaccount._objrep;
    }
    public static implicit operator BankAccount(MATLABObject _obj){
        BankAccount bankaccount = new BankAccount();
        bankaccount._objrep = _obj;
#pragma warning disable CS8601, CS8602
        bankaccount._matlab = typeof(MATLABObject).GetField("Matlab", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(_obj);
#pragma warning restore
        return bankaccount;
    }
}

Class inheritance: The C# BankAccount class does not explicitly inherit from a standard C# class that mimics a MATLAB handle class. Instead, the characteristics of a MATLAB handle class are flattened into the C# representation. This approach involves selectively integrating a subset of supported methods into the generated C# code. By incorporating these methods, the C# BankAccount class emulates the behavior of a MATLAB handle class.

Constructors and overloaded operators: These operators provide similar functionalities to a MATLAB handle class, which supports comparison operations.

public static bool operator >= (BankAccount obj1, BankAccount obj2)
public static bool operator <= (BankAccount obj1, BankAccount obj2)
public static bool operator != (BankAccount obj1, BankAccount obj2)
public static bool operator == (BankAccount obj1, BankAccount obj2)
public static bool operator > (BankAccount obj1, BankAccount obj2)
public static bool operator < (BankAccount obj1, BankAccount obj2)

Method mapping: The C# methods (deposit, withdraw, and checkBalance) correspond to the MATLAB class methods.

MATLAB Signature

C# Signature

function deposit(obj, amount)
public void deposit(double amount)
function withdraw(obj, amount)
public void withdraw(double amount)
function bal = checkBalance(obj)
public void checkBalance()