How can I make a .NET object friendlier to PowerShell?
I have a.NET class that I want to use from both C# and PowerShell. Cut down to its bare bones, it’s something like this:
class Record
{
Dictionary<string, string> _fields = new Dictionary<string, string>();
public IDictionary<string, string> Fields { get { return _fields; } }
//...other stuff...
}
So I get a Record from somewhere and I can do record.Fields["foo"] = "bar" to modify/add fields from either C# or PowerShell. Works great.
But I’d like to make it a little more PowerShell-friendly. I want to do record.foo = "bar" and have it call the appropriate getters and setters. (I’d like to do the same with C# at some point, probably using dynamic, but that’s a separate fun project). Seems like I need a wrapping proxy class.
I know about add-member but I am worried that it would be slow and use a lot of memory when dealing with tens of thousands of records. I also don’t know how to have it handle record.somenewvalue = "abc".
I’m guessing that I want t开发者_如何转开发o create my proxy class in C#, using the facilities in System.Management.Automation or Microsoft.PowerShell but don’t quite know where to start. Can anyone help point me in the right direction?
I figured out one way to do it. Just make Record itself a dictionary. Here's some sample code:
$source = @"
using System.Collections.Generic;
public class Record : Dictionary<string, object>
{
}
"@
add-type -typedefinition $source
$x = new-object record
$x.add('abc', '1') # add
$x['def'] = '2' # indexer
$x.ghi = 3 # "member"
$x
It outputs:
Key Value
--- -----
abc 1
def 2
ghi 3
Here is some code illustrating three suggestions:
public class Record: IEnumerable<KeyValuePair<string, string>>
{
public Record() {}
public Record (Hashtable ht)
{
foreach (var key in ht.Keys)
{
this[key.ToString()] = ht[key].ToString();
}
}
Dictionary<string, string> _fields = new Dictionary<string, string>();
public IDictionary<string, string> Fields { get { return _fields; } }
public string this[string fieldName] {
get {
return _fields[fieldName];
}
set { _fields[fieldName] = value; }
}
//...other stuff...
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _fields.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
- Implement this indexers
- Implement IEnumerable> to improve how powershell prints out the output
- A constructor taking a single hashtable as a parameter allows you to use @{} notation on initial assignment.
Now here is some powershell illustrating the improved usage:
Add-Type -Path 'C:\Users\zippy\Documents\Visual Studio 2010\Projects\ConsoleApplication1\ClassLibrary1\bin\Debug\ClassLibrary1.dll'
[ClassLibrary1.Record] $foo = New-Object ClassLibrary1.Record;
$foo["bar"]
$foo["bar"] = "value";
$foo["bar"]
[ClassLibrary1.Record] $foo2 = @{
"bar"= "value";
"something"= "to talk about";
};
$foo2;
精彩评论