tags:

views:

654

answers:

4

Does C# have anything like Python's __getattr__?

I have a class with many properties, and they all share the same accessor code. I would like to be able to drop the individual accessors entirely, just like in Python.

Here's what my code looks like now:

class Foo
{
 protected bool Get(string name, bool def)
 {
  try {
   return client.Get(name);
  } catch {
   return def;
  }
 }

 public bool Bar
 {
  get { return Get("bar", true); }
  set { client.Set("bar", value); }
 }

 public bool Baz
 {
  get { return Get("baz", false); }
  set { client.Set("baz", value); }
 }
}

And here's what I'd like:

class Foo
{
 public bool Get(string name)
 {
  try {
   return client.Get(name);
  } catch {
   // Look-up default value in hash table and return it
  }
 }

 public void Set(string name, object value)
 {
  client.Set(name, value)
 }
}

Is there any way to achieve this in C# without calling Get directly?

Thanks,

A: 

This is not the same as what you get in Python with dynamic property names, but you might find it useful:

using System;
using System.Collections.Generic;

namespace Program
{
    class Program
    {
        static void Main(string[] args)
        {
            MyList<int> list = new MyList<int>();
            // Add a property with setter.
            list["One"] = 1;
            // Add a property with getter which takes default value.
            int two = list["Two", 2];
            Console.WriteLine("One={0}", list["One"]);
            Console.WriteLine("Two={0} / {1}", two, list["Two"]);
            try
            {
                Console.WriteLine("Three={0}", list["Three"]);
            }
            catch
            {
                Console.WriteLine("Three does not exist.");
            }
        }
        class MyList<T>
        {
            Dictionary<string, T> dictionary = new Dictionary<string,T>();

            internal T this[string property, T def]
            {
                get
                {
                    T value;
                    if (!dictionary.TryGetValue(property, out value))
                        dictionary.Add(property, value = def);
                    return value;
                }
            }
            internal T this[string property]
            {
                get
                {
                    return dictionary[property];
                }
                set
                {
                    dictionary[property] = value;
                }
            }
        }
    }
}
Joe Erickson
I'm not sure I understand what you mean by TryGet (client is a library class, so I can't change its behavior), but performance isn't very important anyway.
Can Berk Güder
For example, System.Collections.Generic.Dictionary has a TryGetValue method which returns true if the key is already there and false otherwise. If the class you are using has no such method, then you have no choice and it's a good thing performance does not matter.
Joe Erickson
OK, I changed my answer to something you might find useful (is it?). Notice that the indexer which takes a default value uses TryGetValue to avoid the try / catch.
Joe Erickson
It's a valid alternative to what I have right now, but I want to keep the properties.
Can Berk Güder
+2  A: 

No. Although C# supports reflection, it is read-only (for loaded assemblies). That means you can't change any methods, properties, or any other metadata. Although you could create a dynamic property, calling it wouldn't be very convenient - it would be even worse than using your Get method. Aside from using a Dictionary<string, object> and an indexer for your class, there's not much else you can do. Anyway, isn't doing a dictionary better if you have that many properties?

Python doesn't check if an attribute exists at "compile-time" (or at least load-time). C# does. That's a fundamental difference between the two languages. In Python you can do:

class my_class:
    pass

my_instance = my_class()
my_instance.my_attr = 1
print(my_instance.my_attr)

In C# you wouldn't be able to do that because C# actually checks if the name my_attr exists at compile-time.

wj32
The properties aren't *that* many (around 20), but it's code repetition anyway, and I don't like the sight of it. =)I want to use properties rather than a dictionary for the same reason. Properties look nicer. Oh, and the client keys might change.
Can Berk Güder
It's impossible. C# doesn't even have a general-purpose #define!
wj32
+1  A: 

I'm not sure, but perhaps the dynamic features of version 4.0 will help you with that. You'll have to wait though...

Martinho Fernandes
Yes, they will. See http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject%28VS.100%29.aspx
wj32
Hehe, with your vote, I now have 1969 rep. What a coincidence, huh?
Martinho Fernandes
+1  A: 

Can I ask: why don't you want the individual properties? That is the idiomatic .NET way of expressing the data associated with an object.

Personally, assuming the data isn't sparse, I would keep the properties and have the overall method use reflection - this will make your compiled code as fast as possible:

    protected T Get<T>(string name, T @default)
    {
        var prop = GetType().GetProperty(name);
        if(prop == null) return @default;
        return (T) prop.GetValue(this, null);
    }

Of course, if you don't care about the properties themselves being defined, then an indexer and lookup (such as dictionary) would be OK. There are also things you might be able to do with postsharp to turn a set of properties into a property-bag - probably not worth it, though.

If you want the properties available for data-binding and runtime discovery, but can't define them at compile-time, then you would need to look at dynamic type descriptors; either ICustomTypeDescriptor or TypeDescriptionProvider - a fair bit of work, but very versatile (let me know if you want more info).

Marc Gravell
I *do* want the individual properties, I just don't want unnecessary code repetition. As I said in response to Joe Erickson's answer, performance isn't that important.
Can Berk Güder
So using my reflection approach above - what is duplicated?
Marc Gravell