0

Given are following classes:

class A
{
    [CustomAttribute(1)]
    public const string X = "x";

    [CustomAttribute(2)]
    public const string Y = "y";

    [CustomAttribute(3)]
    public const string Z = "z";
}

class B
{
    [CustomAttribute(4)]
    public const string P = "p";

    [CustomAttribute(5)]
    public const string Q = "q";

    [CustomAttribute(6)]
    public const string Z = "z";
}

Note duplication of Z constant, which has different CustomAttribute parameter.

I would like to, using reflection, iterate both classes and produce a dictionary-like collection with following properties:

dict.CustomGet(A.Z) == 3
dict.CustomGet(B.Z) == 6

Now, I'm aware that I could fairly easily do:

dict.CustomGet(()=>A.Z)

implemented as:

public int CustomGet(Expression<Func<string>> expr){...}

and use Expression object to find out which class and field I'm accessing, and have internal collection like Dictionary<Type,Dictionary<string,int>>or maybe even Dictionary<FieldInfo,int>, but it requires me to write that questionable ()=>Class.ConstantName each time.

Note, that I cannot change the string literal values to be unique.

Above is my current problem, and I think my question is: can I in another way than Expression tell C# to pass an unique object to CustomGet instead of non-unique string?

Side note: I thought of comparing references of the passed strings, but due to interning I think it is quite likely that "z" will ReferenceEqual another "z".

Final note: This is mostly for fun, I can (and likely will) avoid the problem altogether, but I like to know what C# limitations are for future reference :)

Gerino
  • 1,943
  • 1
  • 16
  • 21

2 Answers2

0

After posting I got an idea, but I'm not 100% convinced how good it is:

Modify the classes to look like this:

class A
{
    [CustomAttribute(1)]
    public readonly MyCustomString X = "x";

    [CustomAttribute(2)]
    public readonly MyCustomString Y = "y";

    [CustomAttribute(3)]
    public readonly MyCustomString Z = "z";
}

Then create type:

public class MyCustomString
{
    private readonly string _value;
    public static implicit operator MyCustomString(string str)
    {
        return new MyCustomString{_value=str;};
    }
    public static implicit operator string(MyCustomString mcs)
    {
        return mcs._value;
    }
}

And now when passed like CustomGet(A.X) I can just use the passed object (public int CustomGet(MyCustomString mcs);) as it will be treated as different, even though it holds the same _value.

How crazy/bad is this?

Gerino
  • 1,943
  • 1
  • 16
  • 21
0

Subclassing the strings is probably the best way of doing it. It ensures that each instance is unique and can be compared uniquely without fear of collision. You will need to make the _value string in MyCustomString not readonly because it needs to be written on object initialization.

If you wanted to further 'expand' it, you could pass in the class itself into the MyCustomString initialization.

eg:

public readonly MyCustomString Z = new MyCustomString(A.class, "Z");

That way you can do a full equality operation between two MyCustomString instances. You will also need to override the GetHashCode function in MyCustomString to get your class to work properly with the Dictionary classes (more detail here)

Community
  • 1
  • 1
Ben Abraham
  • 482
  • 5
  • 10