Let us say I have a BankBalance class in C# that defines a function DebitTransaction. DebitTransaction performs following operation. If balance > amount to be debited, it debits the amount and also notifies the caller that operation is successful. Otherwise it notifies the caller that operation failed.
Below is the code inside a C# DLL as below
using System;
namespace BankSystem
{
public class BankBalance
{
public int Balance { get; set; } = 1000;
// Define a delegate that informs the caller about the transaction status
public delegate void DebitTransactionStatus(string message);
public void DebitTransaction(int Debit, DebitTransactionStatus debitTransactionStatus)
{
if (Balance >= Debit)
{
Balance -= Debit;
debitTransactionStatus("Amount is Debited Successfully");
}
else
{
debitTransactionStatus("Not enough balance to perform debit operation");
}
}
}
}
Below code is the caller (C# exe calling C# DLL function DebitTransaction)which requests a debit operation.
using System;
using BankSystem;
namespace Transaction
{
class DebitTransaction
{
static void ShowDebitTransactionStatus(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
BankBalance bankBalance = new BankBalance();
BankBalance.DebitTransactionStatus debitTransactionStatus = new BankBalance.DebitTransactionStatus(ShowDebitTransactionStatus);
bankBalance.DebitTransaction(500, debitTransactionStatus);
bankBalance.DebitTransaction(600, debitTransactionStatus);
}
}
}
Above program works and here is the output
Amount is Debited Successfully
Not enough balance to perform debit operation
I need to call C# DLL function DebitTransaction function from powershell (calling C# delegate & then register for the call back) to get the similar result
This would be a great help for me, since I am a C++/C# developer but now doing Automation stuffs in PowerShell due to lack of resources in our company. I am also a newbie in PowerShell. (The Project I am working on has many exported functions which has delegates & callbacks)
I tried couple of links, but those are not working for me. https://renenyffenegger.ch/notes/Microsoft/dot-net/namespaces-classes/System/Delegate/CreateDelegate/example-PowerShell/index
PowerShell can convert a ScriptBlock {...}
into a DebitTransactionStatus
delegate in this case, updating the code a bit for the demo so it's compatible with C# 5 and compiles in Windows PowerShell 5.1.
Add-Type @'
namespace BankSystem
{
public class BankBalance
{
private int _balance = 1000;
public int Balance
{
get { return _balance; }
set { _balance = value; }
}
// Define a delegate that informs the caller about the transaction status
public delegate void DebitTransactionStatus(string message);
public void DebitTransaction(
int Debit,
DebitTransactionStatus debitTransactionStatus)
{
if (Balance >= Debit)
{
Balance -= Debit;
debitTransactionStatus("Amount is Debited Successfully");
return;
}
debitTransactionStatus("Not enough balance to perform debit operation");
}
}
}
'@
$balance = [BankSystem.BankBalance]::new()
$balance.DebitTransaction(1000, { param($message) Write-Host $message })
# Amount is Debited Successfully
$balance.DebitTransaction(1000, { param($message) Write-Host $message })
# Not enough balance to perform debit operation
If you have it already compiled you can use Add-Type
targeting the path to your assembly instead of embedding the C# code, for example:
Add-Type -Path path\to\myAssembly.dll
$balance = [BankSystem.BankBalance]::new()
$balance.DebitTransaction(1000, { param($message) Write-Host $message })
# Amount is Debited Successfully
$balance.DebitTransaction(1000, { param($message) Write-Host $message })
# Not enough balance to perform debit operation
Newer versions of PowerShell 7 will also allow you to pass-in a PSMethod
as your delegate:
class DebitTransaction {
static [void] ShowDebitTransactionStatus([string] $message) {
[System.Console]::WriteLine($message)
}
}
$balance = [BankSystem.BankBalance]::new()
$balance.DebitTransaction(1000, [DebitTransaction]::ShowDebitTransactionStatus)
# Amount is Debited Successfully
$balance.DebitTransaction(1000, [DebitTransaction]::ShowDebitTransactionStatus)
# Not enough balance to perform debit operation
In older versions this approach is more cumbersome... You have to create a delegate from the MethodInfo
:
$delegate = [DebitTransaction].GetMethod('ShowDebitTransactionStatus').
CreateDelegate([BankSystem.BankBalance+DebitTransactionStatus])
$balance = [BankSystem.BankBalance]::new()
$balance.DebitTransaction(1000, $delegate)
# Amount is Debited Successfully
$balance.DebitTransaction(1000, $delegate)
# Not enough balance to perform debit operation
As suggested in comments, using an event
that a consumer can subscribe to would be the standard thing to do.
Here is a demo of how it'd look like:
namespace BankSystem;
public class BankBalance
{
public int Balance { get; set; } = 1000;
// Define a delegate that informs the caller about the transaction status
public delegate void DebitTransactionStatus(string message);
// Define an event of type DebitTransactionStatus
public event DebitTransactionStatus? DebitRegistered;
public void DebitTransaction(int Debit)
{
if (Balance >= Debit)
{
Balance -= Debit;
// if there is a subscriber send this message
DebitRegistered?.Invoke("Amount is Debited Successfully");
return;
}
// if there is a subscriber send this message
DebitRegistered?.Invoke("Not enough balance to perform debit operation");
}
}
Then on the consumer side (PowerShell), you'd be using Register-ObjectEvent
subscribing to the DebitRegistered
event:
$balance = [BankSystem.BankBalance]::new()
$registerObjectEventSplat = @{
InputObject = $balance
EventName = 'DebitRegistered'
Action = {
param($message)
Write-Host $message
}
}
$evt = Register-ObjectEvent @registerObjectEventSplat
$balance.DebitTransaction(1000)
# Amount is Debited Successfully
$balance.DebitTransaction(1000)
# Not enough balance to perform debit operation