Powershell Class Requests

It does not appear that I can use Parameter validation inside a class.

When I try to add parameter validation it throws a syntax error:
Attribute ‘ValidateSet’ is not valid on this declaration. It is valid on ‘Property, Field’ declarations only.

It also does not seem to like using the [System.Enum]::GetNames() function (which returns the same type expected by the ValidateSet attribute), yet demands that ‘Attribute argument must be a constant’ when you do something like:

enum LogProviders { Uninitialized = 0 TextFile = 1 EventLog = 2 Database = 3 } MyClassConstructor([ValidateSet($([System.Enum]::GetNames([LogProviders])))][LogProviders]$p, [string]$cs)

I would also like to be able to reference a class from a different ps1 or psm1 file to make sharing code between projects easier.
Ultimately what I would like to see is that adding a using ‘myclass.psm1’ to the top of the class would have the same effect as:

CompanyService.ps1 (example only)

class Company {
[string]$CompanyName

Company([string]$name) {
    $this.CompanyName = $name;
}

}

$syncHash.CompanyService = [CompanyService]::new($syncHash.CoName);
$syncHash.Error = $Error;

MyScript.ps1

[string]$CoServPath = “$PSScriptRoot\CompanyService.ps1”;
$syncHash = [hashtable]::Synchronized(@{});
$syncHash.CompanyService = $null;

$rsData = [RunspaceFactory]::CreateRunspace();
$rsData.Name = $rsName;
$rsData.Open();

set the hash table that will be passed back and forth between threads

$rsData.SessionStateProxy.SetVariable(“syncHash”,$syncHash);

create the new powershell thread and give it our runspace

$psComp = [PowerShell]::Create();
$psComp.Runspace = $rsData;

add the script that loads the company service

$psComp.AddScript($CoServPath) | Out-Null;

#execute the company service script in a separate thread
$result = $psComp.BeginInvoke();

wait for the script to finish

While (-not $result.IsCompleted) { Start-Sleep -Milliseconds 100; };

#set the error message to the label content
if ($psComp.InvocationStateInfo.Reason.Message -ne $null)
{
#dostuff
$lblCoPickFooter.Content = $psComp.InvocationStateInfo.Reason.Message.ToString();

} else
{
# we got something, process the results
$psComp.EndInvoke($result);
}
#cleanup thread and runspace
$rsData.Close();
$psComp.Dispose();

This code shells out to a new thread and passes back the object in a synchronized hash table. What would be nice is if i could just add the using statement and then create new instances of the class without having to do all the syncHash mumbo jumbo.

Anyways, thanks for making such a great language that is fun to work with!

So, just so we’re all on the same page, PowerShell.org is an independent, community-owned and -operated website. It isn’t run by Microsoft, and the product team members don’t really lurk here.

You might consider https://windowsserver.uservoice.com/forums/301869-powershell, which is the official feedback page for the Windows PowerShell product.

I’ll point out in advance that classes in PowerShell v5 are still in very early days. They were implemented primarily to help make DSC resource authoring more consistent; they’ve got a lot of growth still ahead of them.

There is a “using module” statement that’s in PSv5 right now, but it’s a little bit flaky to use, and I’d recommend not trying to export / share classes across modules yet if you can avoid it. However, this doesn’t prevent you from return instances of the class. That works just fine, and you should be able to just output your [CompanyService] object down the pipeline without needing the synchronized hashtable.

The only thing the “using module” statement will help with us resolving the class name in expressions such as [ClassName]::SomeStaticMethod() .

As to your parameter validation question, I don’t think you need to do it. Casting your property as your enum type sort of handles the validation for you. So for this class:

enum LogProviders {
 Uninitialized = 0
 TextFile = 1
 EventLog = 2
 Database = 3
 }

Class MyClass {
    [LogProviders]$p
    [String]$cs

    MyClass(){}
    MyClass(
        [LogProviders]$p,
        [string]$cs
    ) {
        $this.p = $p
        $this.cs = $cs
    }
}

These parameters work:

[MyClass]::New('TextFile','anystring')
[MyClass]::New('Uninitialized','anystring')
[MyClass]::New('Database','anystring')

But this one throws a friendly error

[MyClass]::New('notinenum','anystring')

Cannot convert argument “p”, with value: “notinenum”, for “.ctor” to type “LogProviders”: “Cannot convert value “notinenum” to type “LogProviders”. Error: “Unable to match the identifier name notinenum to a valid
enumerator name. Specify one of the following enumerator names and try again:
Uninitialized, TextFile, EventLog, Database””