11

I have a class hierarchy that represents a JSON based API. There is a generic factory that calls and deserializes the api into classes using .NET 4 (no 3rd party libs). I am trying to avoid having to instantiate the class to retrieve a read-only piece of information that is unique to each class.

I had thought (until I started reading this and this, ...) I would associate a static URL with a base class/interface and then set it in the derived class's constructor. Something like (this example will not work):

abstract class url {
  public abstract static string URL; // This is invalid syntax!
}

class b : url {
  static b () { URL = "http://www.example.com/api/x/?y=1"; }
}

class c: url {
  static c () { URL = "http://www.example.com/api/z"; }
}

// ... so the factory can do something like ...
b result = doJSONRequest<b>(b.URL);

This doesn't work. The static field can't be abstract, nor can it be uniquely set in b and c as the static variable is stored in the class it is defined in (url in this case).

How can I have a read only item associated with a class such that you can access the item (etc.) without having to instantiate the class?

Community
  • 1
  • 1
AlG
  • 14,697
  • 4
  • 41
  • 54
  • 1
    why does the URL have to be static? – MBen Dec 18 '12 at 17:50
  • 2
    Static items belong to a type and cannot be overridden by inheriting types. – Oded Dec 18 '12 at 17:54
  • 1
    MBen, it's constant and I wanted to avoid instantiating an object to access a constant field. Oded, I know, thus why I'm asking for help figuring out what else can be done. – AlG Dec 18 '12 at 17:56
  • 1
    Exactly. Fundamentally broken Approach, so you end up fighting the language. – TomTom Dec 18 '12 at 17:57
  • Related: http://stackoverflow.com/questions/1776369/c-inheriting-separate-static-members-for-derived-classes – H H Dec 18 '12 at 17:59
  • @TomTom Agreed. Thus what is a better way? I noted Oded's point in the original question. I knew it was broken, but I'm not seeing how to "fix" it - well, not liking the fix anyway. – AlG Dec 18 '12 at 18:02

3 Answers3

11

I've implemented a pattern like this to help remind me of constants that I need to setup per derived class that need to be statically accessible:

public abstract class Foo
{
    public abstract string Bar { get; }
}

public class Derived : Foo
{
    public const string Constant = "value";
    public override string Bar
    {
        get { return Derived.Constant; }
    }
}

I've even found that after implementing this pattern that the polymorphic use of the constant to be just as helpful.

JG in SD
  • 5,427
  • 3
  • 34
  • 46
1

I understand you don't want to have to ask a instance but keep the method static. This is impossible, static field is loaded once in a module, and cannot be inherited.
I think the only way is to store a dictionary in a helper class, with the type as a key. Like this

class Helper
{
    static Dictionary<Type,string> _urls;
    public static string GetUrl(Type ofType)
    {
        return _urls[ofType];
    }

    public static void AddUrl(Type ofType, string url)
    {
        _urls.Add(ofType,url);
    }
}
class b
{
    static b(){ Helper.AddUrl(typeof(b),"  ");}
}
class Program
{
    b result= doJSONRequest<b>(Helper.GetUrl(typeof(b));
}

Or you can decorate the desired types with a custom attribute and store the data in that attribute. Like this

class UrlAttribute:Attribute
{
    public string Url{get;private set;}
    public UrlAttribute(string url){Url=url;}
}
[Url("someurl")]
class b { }
class Program
{
    void Main()
    {
        UrlAttribute attr = (UrlAttribute)Attribute.GetCustomAttribute(typeof(b), typeof(UrlAttribute));
        //in dot net 4.5 you can ask the type itself
        UrlAttribute attr = (UrlAttribute)typeof(b).GetCustomAttribute(typeof(UrlAttribute));
        //now you can write that...
        b result = doJSONRequest<b>(attr.Url);
    }
    //or even you can do that in doJSONRequest itself
    public T doJSONRequest<T>()
    {
         UrlAttribute attr = (UrlAttribute)typeof(T).GetCustomAttribute(typeof(UrlAttribute));
        ...
        //call it: b result=doJSONRequest<b>();
    } 
}

Of course you can pass on them all by reflection and initialize a dictionary, see this question.

Community
  • 1
  • 1
RoadBump
  • 733
  • 7
  • 16
  • I tried this out; it relies on something causing b's static constructor to be called. As it turs out, that wasn't done by the time the generic function call was it causing a missing key exception. – AlG Dec 18 '12 at 19:13
  • static ctor is called before the first instance is created or method invoked. Maybe you can create one and then populate it with data from the url, which will cause the ctor to be called prior to the url fetch. – RoadBump Dec 18 '12 at 19:51
  • Oops, excuse me. If i understand your problem, you're getting the object by deserialization from JSON, and you need to know the url before any object is created. I'll try to think on something. – RoadBump Dec 18 '12 at 20:03
  • 1
    Well, all you have is just an uninitialized type. The only thing you can do is to hard-code a list of their types and urls, or read this from database. If your project is large with many object types, consider to decorate the desired types with a custom attribute, and then you can pass on them all by reflection. You may store the url into the attribute. this will prevent a ugly, hard-to-maintain list in a single location. – RoadBump Dec 18 '12 at 20:21
  • Thanks Road! I think the attribute is the way I'll end up going, need to prototype it out. You should submit it as an answer! – AlG Dec 19 '12 at 12:27
-2

You can do it like this, without a static field. Because static field belong to a type !

abstract class url
{
    public virtual string URL { get; } // This is invalid syntax!
}

class b : url
{
    public override string URL
    {
        get { return "http://www.example.com/api/x/?y=1"; }
    }
}

class c : url
{
    public override string URL
    {
        get { return "http://www.example.com/api/z"; }
    }

}
jordsti
  • 746
  • 8
  • 12