An Equals
method on a specific class might be used to implement a PowerShell operator as -eq
:
class TestClass {
hidden [String] $_Value
TestClass([String]$Value) { $this._Value = $Value }
[Bool] Equals($Test, [StringComparison]$StringComparison) {
return $this._Value.Equals([String]$Test._Value, $StringComparison)
}
[Bool] Equals($Test) {
return $this.Equals($Test, [StringComparison]::CurrentCultureIgnoreCase)
}
}
$a1Lower = [TestClass]'a'
$b1Lower = [TestClass]'b'
$a2Lower = [TestClass]'a'
$a2Upper = [TestClass]'A'
$a1Lower -eq $b1Lower # False
$a1Lower -eq $a2Lower # True
$a1Lower -eq $a2Upper # True
Is it also possible to define a method for case sensitive operators such as -ceq
, so that I also might do something like:
$a1Lower -ceq $a2Lower # True
$a1Lower -ceq $a2Upper # True (expected False)
An Equals
method on a specific class might be used to implement a PowerShell operator as -eq
:
class TestClass {
hidden [String] $_Value
TestClass([String]$Value) { $this._Value = $Value }
[Bool] Equals($Test, [StringComparison]$StringComparison) {
return $this._Value.Equals([String]$Test._Value, $StringComparison)
}
[Bool] Equals($Test) {
return $this.Equals($Test, [StringComparison]::CurrentCultureIgnoreCase)
}
}
$a1Lower = [TestClass]'a'
$b1Lower = [TestClass]'b'
$a2Lower = [TestClass]'a'
$a2Upper = [TestClass]'A'
$a1Lower -eq $b1Lower # False
$a1Lower -eq $a2Lower # True
$a1Lower -eq $a2Upper # True
Is it also possible to define a method for case sensitive operators such as -ceq
, so that I also might do something like:
$a1Lower -ceq $a2Lower # True
$a1Lower -ceq $a2Upper # True (expected False)
Share
Improve this question
edited Mar 15 at 5:57
M. Justin
21.9k11 gold badges130 silver badges164 bronze badges
asked Mar 11 at 10:16
iRoniRon
24k10 gold badges58 silver badges99 bronze badges
2
- Use Where -object : $a1Lower | Where-Object $_ -ceq $a2Lower – jdweng Commented Mar 11 at 12:03
- 1 I don't think you can... What you can do is have a ctor overload where you take a StringComparer as argument and the use that comparer for your equality checks. You should also override GetHashCode(). – Santiago Squarzon Commented Mar 11 at 12:46
1 Answer
Reset to default 3I don't think you can, -ceq
is a pure PowerShell implementation. The closest I can think of is to instantiate your classes using a StringComparer
, here you could either have 2 .ctor
overloads or a factory method to create instances that are case-sensitive (demo shows the factory method approach). You should also consider overriding the GetHashCode()
method so your class works well on data types that use it.
class TestClass {
hidden [String] $_Value
[System.StringComparer] $Comparer =
[System.StringComparer]::InvariantCultureIgnoreCase
TestClass([String] $Value) {
$this._Value = $Value
}
static [TestClass] CreateCaseSensitive([string] $Value) {
$test = [TestClass]::new($Value)
$test.Comparer = [System.StringComparer]::InvariantCulture
return $test
}
[Bool] Equals($That) {
if ($testClass = $That -as [TestClass]) {
return $this.Comparer.Equals($this._Value, $testClass._Value)
}
throw [System.ArgumentException] 'nope...'
}
[int] GetHashCode() {
return $this.Comparer.GetHashCode($this._Value)
}
}
[TestClass] 'a' -eq 'A' # True
[TestClass]::CreateCaseSensitive('a') -eq 'A' # False
$set = [System.Collections.Generic.HashSet[TestClass]]::new()
$set.Add('a') # true
$set.Add('A') # false
$set.Add([TestClass]::CreateCaseSensitive('A')) # true
If you're curious how does it translates to C# when you use -ceq
, you can have a look at the ScriptBlockDisassembler Module:
{
$a1 = [TestClass]::new('a')
$a2 = [TestClass]::new('A')
$a1 -ceq $a2
} | Get-ScriptBlockDisassembly -Minimal
As you can see, the expression for equality comparison is created from PSBinaryOperationBinder.Get
, correctly, using the argument ignoreCase: false
which seem to be completely ignored during the comparison. From here, you can also check DynamicMetaObject BinaryEqualityComparison(DynamicMetaObject target, DynamicMetaObject arg)
, where you can see checks for .LimitType
being of type char
or string
, otherwise ignored and cast to object
which explains why -ceq
is completely ignored.
// ScriptBlock.EndBlock
try
{
locals.Item009 = Fake.Dynamic<Func<CallSite, object, object>>(
PSVariableAssignmentBinder.Get())(
Fake.Dynamic<Func<CallSite, Type, string, object>>(
PSCreateInstanceBinder.Get(
new CallInfo(1),
PSMethodInvocationConstraints.Default, publicTypeOnly: true))(
Fake.Const<Type>(typeof(TestClass)), "a"));
locals.Item010 = Fake.Dynamic<Func<CallSite, object, object>>(
PSVariableAssignmentBinder.Get())(
Fake.Dynamic<Func<CallSite, Type, string, object>>(
PSCreateInstanceBinder.Get(
new CallInfo(1),
PSMethodInvocationConstraints.Default, publicTypeOnly: true))(
Fake.Const<Type>(typeof(TestClass)), "A"));
Fake.Dynamic<Action<CallSite, object, Pipe, ExecutionContext>>(
PSPipeWriterBinder.Get())(
Fake.Dynamic<Func<CallSite, object, object, object>>(
PSBinaryOperationBinder.Get(
ExpressionType.Equal, ignoreCase: false, scalarCompare: false))
(locals.Item009, locals.Item010),
funcContext._outputPipe,
context);
}
catch (FlowControlException)
{
throw;
}
catch (Exception exception)
{
ExceptionHandlingOps.CheckActionPreference(funcContext, exception);
}