How to store int[] array in application Settings - c#

I'm creating a simple windows Forms application using C# express 2008. I'm an experienced C++ developer, but I am pretty much brand new to C# and .NET.
I'm currently storing some of my simple application settings using the settings designer and code like this:
// Store setting
Properties.Settings.Default.TargetLocation = txtLocation.Text;
...
// Restore setting
txtLocation.Text = Properties.Settings.Default.TargetLocation;
Now I'd like to store either an array of ints ( int[] ), or possibly a List of ints ( List< int > ), as a setting. However, I can't figure out how to do this. I've searched the documentation, stackoverflow, and google, and I cannot find a decent explanation of how to do this.
My hunch based on the sparse examples I've found is that I have to create a class that is serializable that wraps my array or List, and then I will be able to use that Type in the settings designer. However, I'm not sure exactly how to do this.

There is also another solution - requires a bit of manual editing of the settings file, but afterwards works fine in VS environment and in the code. And requires no additional functions or wrappers.
The thing is, that VS allows to serialize int[] type by default in the settings file - it just doesn't allow you to select it by default.
So, create a setting with desired name (e.g. SomeTestSetting) and make it of any type (e.g. string by default).
Save the changes.
Now go to your project folder and open the "Properties\Settings.settings" file with text editor (Notepad, for example) Or you can open it in VS by right-clicking in Solution Explorer on " -> Properties -> Settings.settings", select "Open With..." and then choose either "XML Editor" or "Source Code (Text) Editor".
In the opened xml settings find your setting (it will look like this):
<Setting Name="SomeTestSetting" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
Change the "Type" param from System.String to System.Int32[]. Now this section will look like this:
<Setting Name="SomeTestSetting" Type="System.Int32[]" Scope="User">
<Value Profile="(Default)" />
</Setting>
Now save changes and re-open project settings - voilà! - We have the setting SomeTestSetting with type System.Int32[] which can be accessed and edited through VS Settings Designer (values too), as well as in the code.

to store:
string value = String.Join(",", intArray.Select(i => i.ToString()).ToArray());
to re-create:
int[] arr = value.Split(',').Select(s => Int32.Parse(s)).ToArray();
Edit: Abel suggestion!

There is one other way to achieve this result that is a lot cleaner in usage but requires more code. My implementing a custom type and type converter the following code is possible:
List<int> array = Settings.Default.Testing;
array.Add(new Random().Next(10000));
Settings.Default.Testing = array;
Settings.Default.Save();
To achieve this you need a type with a type converter that allows conversion to and from strings. You do this by decorating the type with the TypeConverterAttribute:
[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray ...
Then implementing this type converter as a derivation of TypeConverter:
class MyNumberArrayConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext ctx, Type type)
{ return (type == typeof(string)); }
public override bool CanConvertFrom(ITypeDescriptorContext ctx, Type type)
{ return (type == typeof(string)); }
public override object ConvertTo(ITypeDescriptorContext ctx, CultureInfo ci, object value, Type type)
{
MyNumberArray arr = value as MyNumberArray;
StringBuilder sb = new StringBuilder();
foreach (int i in arr)
sb.Append(i).Append(',');
return sb.ToString(0, Math.Max(0, sb.Length - 1));
}
public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data)
{
List<int> arr = new List<int>();
if (data != null)
{
foreach (string txt in data.ToString().Split(','))
arr.Add(int.Parse(txt));
}
return new MyNumberArray(arr);
}
}
By providing some convenience methods on the MyNumberArray class we can then safely assign to and from List, the complete class would look something like:
[TypeConverter(typeof(MyNumberArrayConverter))]
public class MyNumberArray : IEnumerable<int>
{
List<int> _values;
public MyNumberArray() { _values = new List<int>(); }
public MyNumberArray(IEnumerable<int> values) { _values = new List<int>(values); }
public static implicit operator List<int>(MyNumberArray arr)
{ return new List<int>(arr._values); }
public static implicit operator MyNumberArray(List<int> values)
{ return new MyNumberArray(values); }
public IEnumerator<int> GetEnumerator()
{ return _values.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return ((IEnumerable)_values).GetEnumerator(); }
}
Lastly, to use this in the settings you add the above classes to an assembly and compile. In your Settings.settings editor you simply click the "Browse" option and select the MyNumberArray class and off you go.
Again this is a lot more code; however, it can be applied to much more complicated types of data than a simple array.

Specify the setting as a System.Collections.ArrayList and then:
Settings.Default.IntArray = new ArrayList(new int[] { 1, 2 });
int[] array = (int[])Settings.Default.IntArray.ToArray(typeof(int));

A simple solution is to set the default value of a setting to null in the property, but in the constructor check if the property is null and if so then set it to its actual default value. So if you wanted an array of ints:
public class ApplicationSettings : ApplicationSettingsBase
{
public ApplicationSettings()
{
if( this.SomeIntArray == null )
this.SomeIntArray = new int[] {1,2,3,4,5,6};
}
[UserScopedSetting()]
[DefaultSettingValue("")]
public int[] SomeIntArray
{
get
{
return (int[])this["SomeIntArray"];
}
set
{
this["SomeIntArray"] = (int[])value;
}
}
}
It feels kind of hacky, but its clean and works as desired since the properties are initialized to their last (or default) settings before the constructor is called.

Used System.Object.
Example:
byte[] arBytes = new byte[] { 10, 20, 30 };
Properties.Settings.Default.KeyObject = arBytes;
Extract:
arBytes = (byte[])Properties.Settings.Default.KeyObject;

I think you are right about serializing your settings. See my answer to this question for a sample:
Techniques for sharing a config between two apps?
You would have a property which is an array, like this:
/// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
[XmlAttribute]
public int [] Numbers { get; set; }

Make some functions that convert a int array in a string, but between each put a character like " " (space).
So if the array is { 1,34,546,56 } the string would be "1 34 645 56"

Related

How to get all names for enum items with same value?

Foo and Bar enum members share the same associated value of 180:
public enum EnumShareValues
{
Default = 0,
Foo = 180,
Bar = 180,
}
This code prints String representation of 'Bar' is 'Foo' which is correct, it returns the first occurence of value 180:
const EnumShareValues bar = EnumShareValues.Bar;
Debug.WriteLine("String representation of 'Bar' is '{0}'", bar);
But is it somehow possible to get both names for enum item with value of 180?
Something like:
// would in this case return "Foo, Bar"
Enum.GetAllNames(typeof(EnumShareValues), EnumShareValues.Bar, ",");
Edit:
Finally after some testing and fiddling I ended with the following code. Thank you for all your answers!
public static IEnumerable<string> GetAllNames<T>(T propValue)
where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T must be an enumerated type");
var allNames = Enum.GetNames(typeof(T))
.Where(name => ((T)Enum.Parse(typeof(T), name)).Equals(propValue));
return allNames;
}
Your enums need to be unique, if you duplicate the id then you end up with the following stored internally - look for the debug window with the values in the collection. I suggest a small refactor of the code to accommodate.
this general-puprose method works (fiddle):
i start from enum names and get corresponding values:
public static class Util
{
public static IEnumerable<string> GetSynonims(this Enum value)
{
Type e = value.GetType();
return Enum.GetNames(e).Where(n => Enum.Parse(e, n).Equals(value));
}
}
usage:
Console.WriteLine(String.Join(", ", EnumShareValues.Bar.GetSynonims()));
prints:
Foo, Bar
There's nothing built in, but you can string a few different methods together easily enough:
var allNames =
Enum.GetNames(typeof (EnumShareValues))
.Where(name => (EnumShareValues)Enum.Parse(typeof (EnumShareValues), name)
== EnumShareValues.Bar);
If we were allowed to restrict generic methods to enums, this would be great to wrap up as a generic helper method. You can, of course, do so, but it will produce runtime errors if you try to use it with a non-enum based type.

Generic Type Comparison, Assignment, And Return Based On Custom Attribute

Don’t know why I can’t figure this out…
I am using a comparison method that takes two types, loops through them, and using reflection builds a report of differences of the fields. This is more or less a detailed comparison of the two types. The types have the same properties. While I am doing the comparison, each of the fields also has a custom attribute associated with it. In the below, the Difference object stores the names, and other properties related to the comparison operation.
To do the comparison, I use the following:
public static List<Difference<T>> Detailed Difference <T>(this T val1, T val2)
{
List< Difference <T>> differences = new List< Difference <T>>();
FieldInfo[] fi = val1.GetType().GetFields();
foreach (FieldInfo f in fi)
{
Difference <T> v = new Difference <T>();
v.Prop = f.Name;
v.ValA = f.GetValue(val1);
v.ValB = f.GetValue(val2);
v.ObjectA = val1;
v.ObjectB = val2;
v.MyCustomAttribute = (MyCustomAttribute Attribute) Attribute.GetCustomAttribute(f, typeof (MyCustomAttribute Attribute));
differences.Add(v)
}
return differences;
}
MyCustomAttribute is an enum that contains FooA and FooB.
If MyCustomAttribute equals FooA, the value of ValA should be used to build a return of type T to add to the method List return. If MyCustomAttribute equals FooB, the value of ValB should be used when building the new type. The problem I am having is the generic doesn’t allow me to instantiate a new type of T, for pretty obvious reasons...so I can’t figure out how to, more or less, map the property values based on reading the custom attribute.
If you include the new constraint, then new T() will be possible. Then you can use FieldInfo.SetValue to set the values in it.
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2)
where T : new()
{
T newValue = new T();
// later...
v.MyCustomAttribute = // whatever it is
if (v.MyCustomAttribute == MyCustomAttribute.FooA)
f.SetValue(newValue, v.ValA);
else if (v.MyCustomAttribute == MyCustomAttribute.FooB)
f.SetValue(newValue, v.ValB);
differences.Add(v);
// other stuff...
}
If this won't work with your types, you'll need something passed in to your method. Since you apparently only need one new object, and always one object, you could just take another T:
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2,
T newValue)
{
// call like
var diff = myClass1.Difference(myClass2, new MyClass(someParam));
For more advanced scenarios, take a Func<T>, so that you can call it whenever needed:
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2,
Func<T> getNewValue)
{
T newValue = getNewValue();
// call like
var diff = myClass1.Difference(myClass2, () => new MyClass(someParam));

Call function in dynamic linq

I'm trying to call a function in a dynamic linq select statement, but im getting error:
No property or field 'A' exists in type 'Tuple2'
Example code:
void Main()
{
var a = new Tuple<int, int>(1,1);
var b = new[]{ a };
var q = b.AsQueryable().Select("A.Test(it.Item1)");
q.Dump();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
How should I change my code to get this working?
If I call built in function Convert.ToInt32 for example it works fine.
var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");
Also how do I cast a property using dynamic linq?
var q = b.AsQueryable().Select("((float)it.Item1)");
I'll say that the dynamic-linq isn't "strong enough" to do these things. It looks for methods only in the given objects and some special classes: Math, Convert, the various base types (int, float, string, ...), Guid, Timespan, DateTime
The list of these types is clearly visible if you use ilspy/reflector on the file. They are in System.Linq.Dynamic.ExpressionParser.predefinedTypes .
Now, clearly I could be wrong, but this works: .Select("Guid.NewGuid().ToString()").Cast<string>().ToArray()
showing that it's quite probable that that is the "correct" list.
There is an article here on how to modify Dynamic LINQ if you are interested http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html
Now, an intelligent man would take the source of dynamic linq and simply expand that array... But here there aren't intelligent men... There are only programmers that want blood! Blood but especially innards!
var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");
FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);
Type[] predefinedTypes = (Type[])field.GetValue(null);
Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type
field.SetValue(null, predefinedTypes);
Do this (with the types you want) BEFORE the first call to Dynamic Linq (because after the first call the methods/properties of these types are cached)
Explanation: we use reflection to add our object(s) to this "special list".
I know there is already an accepted answer on this but it did not work for me. I am using Dynamic Linq 1.1.4. I wanted to do a query like this
$.GetNewestRisk() == null
Where GetNewestRisk() is a public method on the object represented by $. I kept getting this error "Error running query, Methods on type 'Patient' are not accessible (at index 2)".
I found in the source code there is a GlobalConfig object that allows a custom provider to be assigned which will hold all of the types you may want to work with. Here is the source code for the custom provider:
public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
public HashSet<Type> GetCustomTypes()
{
HashSet<Type> types = new HashSet<Type>();
types.Add(typeof(Patient));
types.Add(typeof(RiskFactorResult));
types.Add(typeof(PatientLabResult));
types.Add(typeof(PatientVital));
return types;
}
}
Here is how I am using it:
System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();
After making this call I am able to call methods on the objects inside of the expression.
#xanatos answer doesn't work for .Net Core version. So I've found something similar related by #Kent on the System.Dynamic.Linq.Core tests DynamicExpressionParserTests written by the library's author himself.
The given TestCustomTypeProviderClass allows you to use the DynamicLinqType class annotation which is pretty usefull for this problem.
To answer to question, you then just needed to defined the class (ensure to annotate with DynamicLinqType) :
[DynamicLinqType]
public static class A
{
public static int Test(int i)
{
return i++;
}
}
Add a customTypeProvider as mentioned above :
private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
private HashSet<Type> _customTypes;
public virtual HashSet<Type> GetCustomTypes()
{
if (_customTypes != null)
{
return _customTypes;
}
_customTypes = new HashSet<Type>(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly }));
return _customTypes;
}
}
and use a ParsingConfig with the configurable Select to call it :
var config = new ParsingConfig
{
CustomTypeProvider = new TestCustomTypeProvider()
};
var q = b.AsQueryable().Select(config, "A.Test(it.Item1)");
#Armand has put together a brilliant solution for this issue, and being the only solution I was able to find regarding this I want to add to it for anyone who tries the same approach.
The class that is marked with...
[DynamicLinqType]
... must be taken into consideration when you run the following line:
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })
In the solution provided above, this assumes the class that contains the function to be evaluated is on the same class the code currently resides in. If the methods are to be used outside of said class, the assembly will need to change.
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { typeof(AnotherClassName).Assembly })
Nothing changes from the solution above, this is just for clarification for anyone attempting to use it.
As regards the current version (1.2.19) of Dynamic LINQ, you will probably get another exception:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum value 'Test' is not defined in enum type 'A'
To make DLINQ know your type 'A', you have two options:
Set up parsing config with your own custom types provider where you directly specify the type 'A'.
Mark your type with the attribute [DynamicLinqType]. If that type is loaded into the current domain (that's the usual case), you don't have to do anything more since the default custom type provider already scans the current AppDomain for types marked with [DynamicLinqType]. And only if that's not the case, i.e. your type is not loaded into the current domain, you have to do something like in that answer.
What if you would like to use both approaches - the first for type 'A' and the second for type 'B'? In that case, you just have to "merge" your type 'A' with the default provider types:
public class DynamicLinqTests
{
[Test]
public void Test()
{
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var parsingConfig = new ParsingConfig
{
ResolveTypesBySimpleName = true,
CustomTypeProvider = new TestCustomTypesProvider()
};
var queryWithA = b.AsQueryable().Select(parsingConfig, "A.Test(it.Item1)");
queryWithA.ToDynamicList();
var queryWithB = b.AsQueryable().Select(parsingConfig, "B.Test(it.Item1)");
queryWithB.ToDynamicList();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
[DynamicLinqType]
public static class B
{
public static int Test(int i)
{
return i++;
}
}
public class TestCustomTypesProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var customTypes = base.GetCustomTypes();
customTypes.Add(typeof(A));
return customTypes;
}
}
}
I may be confused but your syntax whereby you are using a string in your Selects doesn't compile for me. The following syntax works:
var q = b.AsQueryable().Select(it => A.Test(it.Item1));
var b = new[]{ a };
The above array is don't know what type of array , and it's not type safe ?
Your values are assigned in variant data type so it's not integer value (I think string value) ,when you get this values in your query must need to convert.toint32() because your class parameter data type is integer
Please try it
var b = new **int**[]{ a };
instead of var b = new[]{ a };
The important hint is here (in bold):
No property or field 'xxx' exists in **type** 'xxx'
And Please look this for previous discussion :
Dynamic Linq - no property or field exists in type 'datarow'
The following works for me:
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var q = b.AsQueryable().Select(it=>A.Test(it.Item1));
var q1 = b.AsQueryable().Select(it => Convert.ToInt32(it.Item1));
var q2 = b.AsQueryable().Select(it => (float) it.Item1);

Why does this work?

I was googling trying to find a way to call Control.DataBindings.Add without using a string literal but getting the property name from the property itself, which I think would be less error prone, at least for my particular case, since I normally let Visual Studio do the renaming when renaming a property. So my code would look something like DataBindings.Add(GetName(myInstance.myObject)... instead of DataBindings.Add("myObject".... So I found this:
static string GetName<T>(T item) where T : class
{
var properties = typeof(T).GetProperties();
if (properties.Length != 1) throw new Exception("Length must be 1");
return properties[0].Name;
}
That would be called, assuming I have a property called One, this way: string name = GetName(new { this.One }); which would give me "One". I have no clue why does it work and whether is safe to use it or not. I don't even know what that new { this.One } means. And I don't know on which case could it happens that properties.Length is not 1.
By the way, I just tested to rename my property One to Two and Visual Studio turned new { this.One } into new { One = this.Two }, which when used with the GetName function gave me "One", which make the whole thing useless since the name I would be passing to Control.DataBindings.Add would be still "One" after renaming the property.
new { this.One } creates an instance of an anonymous type with one Property, which is, because you didn't specify a name, called "One". That's why it works.
if you use new { One = this.Two }, you give the property the name "One". If you would leave out the part "One = ", it would work again.
However, the method you are using might be misunderstood if one does not know how it's intended to be used and if one does not call it using an anonymous type.
There is another way if you don't want to use string literals, here is one of the examples you can find on the web:
http://www.codeproject.com/Tips/57234/Getting-Property-Name-using-LINQ.aspx
No, you do not have to stick to string literals:
public static class ControlBindingsCollectionExtensions
{
public static void Add<T>(this ControlBindingsCollection instance, Expression<Func<T, object>> property)
{
var body = property.Body as UnaryExpression;
var member = body.Operand as MemberExpression;
var name = member.Member.Name;
instance.Add(name);
}
}
Usage:
Control.DataBindings.Add<MyClass>(m => m.MyProperty);

Autovivification in C#

Trying to wrap my head around perl's Autovivification and based on what it sounds like, It seems to work similar to dynamics in C# as a dynamic object is not assigned a type until runtime or, am I totally off here. If so then is there a comparable idea that I can bridge off of in C# that makes sense?
Edit
Okay so I'm apparently way off. So as second part of the 2 part question, is there anything conceptually comparable in C#? To be clear I'm looking for a concept in C# that is comparable to Autovivification. Doesn't have to be exactly the same but close enough conceptually to make sense. And as I stated eariler I am by no means a perl hacker or python hacker by any stretch of the imagination but, I am familar with c based languages C, C++, C#, java, javascript. I was thinking of C#'s dynamics but, as of right now I'm thinking lazy loading based on the info here if that helps....
I can't speak to C#, but in layman's terms, Perl's autovivification is the process of creating a container object out of an undefined value as soon as it is needed.
Despite most of Perl being quite dynamic, Perl's dereferencing syntax unambiguously specifies the type of the reference at compile time. This allows the interpreter to know what it needs out of a variable before the variable is ever defined.
my $var; # undefined
# to autovivify to an array:
#$var = 1..5; # # here implies ARRAY
$$var[4] = 5; # square brackets imply ARRAY
$#$var; # $# implies ARRAY (returns the last index number)
# to autovivify to a hash:
%$var = (a => 1); # % implies HASH
$$var{asdf} = 5; # curly braces imply HASH
This list could be longer, but should give you an idea.
So basically, when you have a line like this:
my $var;
$var->[1]{x}[3]{asdf}
Perl looks on the right side of the -> and sees square braces. This means that the invocant $var must be an array reference. Since the invocant is undefined, Perl creates a new array and installs its reference into $var. This same process is then repeated for every subsequent dereferencing.
So the line above really means:
(((($var //= [])->[1] //= {})->{x} //= [])->[3] //= {})->{asdf};
which is fairly hideous, and hence autovivification. (//= is the defined-or assignment operator in perl 5.10+)
Update:
As per cjm's comment, to put this into general non-perl terms, to achieve autovivification in another language, you need a lazy object that supports indexing via [...] and {...}. When either of these indexing operations are performed, the object replaces itself with either an array or hash. Every time the object is then accessed, if the cell is empty, it should return another lazy object.
obj = new lazy_obj()
level1 = obj[4] # sets obj to be an array, returns a new lazy_obj for level1
level2 = level1{asdf} # sets level1 (and obj[4]) to a hash,
# returns a new lazy_obj for level2
So basically you need two things, the ability to create an object that supports indexing with both array and hash subscripts (or the equivalent), and a mechanism such that an object can replace itself in memory with another object (or that can lock itself to one interpretation, and then store the new object internally.
Something like the following pseudo-code could be a start:
class autoviv {
private var content;
method array_subscript (idx) {
if (!content) {
content = new Array();
}
if (typeof content == Array) {
if (exists content[idx]) return content[idx];
return content[idx] = new autoviv();
} else {
throw error
}
}
method hash_subscript (idx) {
if (!content) {
content = new Hash();
}
if (typeof content == Hash) {
if (exists content{idx}) return content{idx};
return content{idx} = new autoviv();
} else {
throw error
}
}
// overload all other access to return undefined, so that the value
// still looks empty for code like:
//
// var auto = new autoviv();
// if (typeof auto[4] == autoviv) {should run}
// if (auto[4]) {should not run}
}
Uri Guttman's autovivification tutorial might be of some use.
Basically, it is the ability of hitherto untouched aggregates and members of aggregates to spring to life upon first use.
For example, I can do this:
#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper;
my #dummy;
push #{ $dummy[0] }, split ' ', 'this that and the other';
push #{ $dummy[1] }, { qw(a b c d) };
print Dumper \#dummy;
Neither $dummy[0] nor $dummy[1] exist before they are dereferenced.
Now, if you are willing to forgo strict (which, you shouldn't be), you can also do things like:
use Data::Dumper;
#$x = qw(a b c d);
print Dumper $x;
whereby the undefined variable $x becomes an array reference because it is being dereferenced as such.
You can implement autovification-like behavior with creating say, an IDictionary<X,Y> that returns (and stores) a new IDictionary<X,Y> (e.g. recursively the same type) when a [] to an unset key occurs. This approach is used in Ruby to great success (an example) -- however, it's really not so useful in a statically typed language because there is no way to "get to" the leaf values cleanly -- at least in context of most existing contracts such as an IDictionary.
With the advent of dynamic, this may be possible in C# to do sanely, but I do not know.
How about something like this for a simple implementation of auto-vivification like behaviour of a Dictionary in C#? Obviously this doesn't handle it in the generic way that Perl does, but I believe that it has the same effect.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// The purpose of this class is to provide a dictionary with auto-vivification behaviour similar to Perl's
// Using dict[index] will succeed regardless of whether index exists in the dictionary or not.
// A default value can be set to be used as an initial value when the key doesn't exist in the dictionary
namespace XMLTest
{
class AutoDictionary<TKey,TValue> : Dictionary<TKey,TValue> {
Object DefaultValue ;
public AutoDictionary(Object DefaultValue) {
this.DefaultValue = DefaultValue;
}
public AutoDictionary() {
this.DefaultValue = null;
}
public new TValue this[TKey index] {
get {
try {
return base[index];
}
catch (KeyNotFoundException) {
base.Add(index, (TValue)DefaultValue);
return (TValue)DefaultValue ;
}
}
set {
try {
base[index] = value ;
}
catch (KeyNotFoundException) {
base.Add(index, value);
}
}
}
}
}
I would recommend using extension methods instead of inheritance.
e.g.:
namespace DictionaryEx
{
public static class Ex
{
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key)
{
var value = default(TV);
if (dict.TryGetValue(key, out value))
{
return value;
}
value = default(TV);
dict[key] = value;
return value;
}
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue)
{
TV value;
if (dict.TryGetValue(key, out value))
{
return value;
}
dict[key] = defaultValue;
return defaultValue;
}
public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, Func<TV> valueFactory)
{
TV value;
if (dict.TryGetValue(key, out value))
{
return value;
}
value = valueFactory();
dict[key] = value;
return value;
}
}
}
Using indexers and C# 4.0 dynamics,
class Tree
{
private IDictionary<string, object> dict = new Dictionary<string, object>();
public dynamic this[string key]
{
get { return dict.ContainsKey(key) ? dict[key] : dict[key] = new Tree(); }
set { dict[key] = value; }
}
}
// Test:
var t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);
DynamicObject can be used for implementing different syntaxes also,
using System;
using System.Collections.Generic;
using System.Dynamic;
class Tree : DynamicObject
{
private IDictionary<object, object> dict = new Dictionary<object, object>();
// for t.first.second.third syntax
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var key = binder.Name;
if (dict.ContainsKey(key))
result = dict[key];
else
dict[key] = result = new Tree();
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dict[binder.Name] = value;
return true;
}
// for t["first"]["second"]["third"] syntax
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
var key = indexes[0];
if (dict.ContainsKey(key))
result = dict[key];
else
dict[key] = result = new Tree();
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
dict[indexes[0]] = value;
return true;
}
}
// Test:
dynamic t = new Tree();
t.first.second.third = "text";
Console.WriteLine(t.first.second.third);
// or,
dynamic t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);

Categories