最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

powershell - How to implement a case-sensitive `Equals` for operators as `-ceq` - Stack Overflow

programmeradmin1浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 3

I 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);
}
发布评论

评论列表(0)

  1. 暂无评论