How can I declare derived "shell" classes that do nothing but act as renames?
Asked Answered
I

7

11

I have two different kinds of strings I'm passing around and using in my code, and the two are closely related, but should not be confused for one another. I thought I could help myself avoid errors by having two classes that are just strings, but with different names so that method signatures (and type incompatibility in general) would enforce the semantic meaning of the two different kinds of strings. At the same time, I didn't want to have to refactor from something = "foo"; to something.Value = "foo"; in a hundred places.

First thought:

private class FirstKind : string { }
private class SecondKind : string { }

The idea being that if I have

void MyMethod(FirstKind varOne, SecondKind varTwo) {...}

, and then try to call it with MyMethod(secondKindVar, firstKindVar);, I'd get a compiler error.

Wall I hit: string is sealed.

Second thought: create generic KindOf<T> class that would do nothing but take in and spit out the value with implicit conversion operators. Like this:

private class KindOf<T>
{
    public T Value { get; set; }
    public KindOf() { Value = default(T); }
    public KindOf(T val) { Value = val; }
    public static implicit operator T(KindOf<T> kindOf) { return kindOf.Value; }
    public static implicit operator KindOf<T>(T value) { return new KindOf<T>(value); }
}

This way I could do something like this:

private class FirstKind : KindOf<string> { }
private class SecondKind : KindOf<string> { }

Wall I hit: nothing from the KindOf<T> class seems to inherit: neither constructors nor operators exist in the derived types, meaning I have to re-implement essentially the whole base class in the derived classes. Code copying. Yuck.

So I feel like I'm missing something basic here, and maybe I'm off in the weeds. Any ideas for what I'm trying to do?

Importune answered 21/6, 2012 at 14:46 Comment(8)
Maybe you could clarify what "two different kinds of strings" means?Stier
Off the top of my head, you might just need to implement the implicit operators and constructors on the subclasses. Yeah, kind of like code copying, but at least it's not employing convoluted process or using inheritance when the inheritance doesn't really communicate anything real (rhetorically, what is a "KindOf" anyway and does it really mean anything?) and is just there to reduce code duplication across classes that have nothing in common besides similar syntax (but different uses)Edric
Agreed, the classes really shouldn't be in the same inheritance hierarchy, especially considering you don't want to be referring to them by a common type, you want them to remain distinct.Infamous
@BryanCrosby Imagine you have unique ID's for records that are string codes rather than integers or Guids. And you want to make sure you don't accidentally pass a PersonId string into a method that's expecting a CompanyId string. To me, that's when something like this is definitely handy.Maize
@Importune Why not use an object to wrap both strings as properties and pass the object around instead? It seems silly to me that you're trying all this simply to preserve the meaning of two particular string values.Bowes
You might find Is there a way to define C# strongly-typed aliases of existing primitive types like string or int? interesting.Flurried
@antisanity It's purely to stop myself from sending one kind of thing where another is expected, as SteveDog said. As far as not wanting to create a wrapper object and pass it around: I already have a lot of code that uses this stuff, and I'm trying to minimize the refactoring effort.Importune
@Flurried Indeed, that's right up this alley.Importune
I
3

To be honest, I wouldn't use the implicit operators at all, or any inheritance for that matter. The whole idea is to have a method that takes a FirstKind. With the implicit operator you can just pass in a string and it'll be converted; this means that you're not forcing the code to explicitly state that it's a first/second kind. I'd stick with two classes that each have just a constructor taking a string and a read only property. You're over complicating it by adding anything else.

If using that property is getting annoying, I could possibly see the implicit conversion to string, from each of the custom classes, but I do believe that an implicit conversion from string would be detrimental. While I agree copy/paste code is generally to be avoided, when it's literally a single line of code, copy-pasting that implicit conversion into both classes will be easier, more effective, and generally more maintainable than trying to use inheritance or anything else to avoid writing it twice.

Infamous answered 21/6, 2012 at 14:58 Comment(2)
+1 Unfortunately, since String is sealed, I'm afraid this is probably the best option.Maize
We agree on this. "Implicit conversion to string" was the idea behind mine, but doing still keeping the classes separate as you suggest.Immodest
E
1

I'm going to make my comment an answer because I think it stands as one.

Off the top of my head, you might just need to implement the implicit operators and constructors on the subclasses. Yeah, kind of like code copying, but at least it's not employing convoluted process or using inheritance when the inheritance doesn't really communicate anything real (rhetorically, what is a "KindOf" anyway and does it really mean anything?) and is just there to reduce code duplication across classes that have nothing in common besides similar syntax (but different uses)

public class FirstKind
{
    public string Value { get; private set; }

    public FirstKind(string value)
    {
        this.Value = value;
    }

    public static implicit operator FirstKind(string value)
    {
        return new FirstKind(value);
    }

    public static implicit operator string(FirstKind value)
    {
        return value.Value;
    }
}

Also, you might want to consider making the class immutable if they're supposed to represent strings. (another implementation detail that might point to perhaps these objects should not be inheriting from a reused KindOf class which, in your implementation, makes them mutable always)

It's not much wiring and leaves your code open for changes in the future (perhaps you wish to add a new property? Add some validation code?) and consumers of the API really don't care that anything is a KindOf just to save on some minor code duplication. (perhaps you can make a snippet to make your life a bit easier?)

Edric answered 21/6, 2012 at 14:59 Comment(0)
A
1

In your question, FirstKind and SecondKind are the types of your string-like variables, and string is the type argument of your KindOf<T> class. A different approach (which avoids the code duplication required by the other answers) is to flip the role of FirstKind and SecondKind, making them empty "marker" classes that are used as type arguments of a String<TKind> type:

public class FirstKind { }
public class SecondKind { }

public struct String<TKind>
{
    private readonly string _value;

    public String(string value)
    {
        _value = value;
    }

    public override string ToString()
    {
        return _value;
    }

    public static implicit operator String<TKind>(string s) // see the note below
    {
        return new String<TKind>(s);
    }

    public static implicit operator string(String<TKind> s)
    {
        return s.ToString();
    }

    public static String<TKind> operator +(String<TKind> s1, String<TKind> s2)
    {
        return new String<TKind>(s1.ToString() + s2.ToString());
    }

    public int Length
    {
        get { return _value.Length; }
    }

    // You can add methods like Substring and Trim that delegate to _value.
}

Now you can declare MyMethod like this:

void MyMethod(String<FirstKind> varOne, String<SecondKind> varTwo)
{
    varOne += varOne; // OK
    varTwo += varTwo; // OK
    varOne = varTwo;  // ERROR
}

Note: I've left in the implicit conversion operator from a plain string. You may want to consider removing it, forcing code to explicitly identify the kind of string: new String<FirstKind>("...").

Adrial answered 21/6, 2012 at 15:4 Comment(1)
Hm, interesting approach. I'll have to consider this.Importune
S
0

I would just do this for strings.

abstract class MyTypedString
{
   protected MyTypedString(string value)
   {
     Value = value;
   }
   public string Value { get; private set; }
}

Then you could finish off this abstract class with your FirstKind and SecondKind. This will keep the object immutable like how strings are. I would not give this any implicit conversion operators.

Saxecoburggotha answered 21/6, 2012 at 14:50 Comment(1)
That's essentially the same as his second example (only less nice).Maize
U
0

Operators will still work, you just need to reference the .Value member of your KindOf class:

FirstKind stringFoo = new FirstKind("foo");
FirstKind stringQux = new FirstKind("qux");

// String concatenation operator
FirstKind stringTar = new FirstKind( stringFoo.Value + stringQux.Value );

However as you say, there will be no "type-checking" if you mix the two, e.g.

SecondKind stringBar = new SecondKind("bar");
FirstKind stringHal = new FirstKind( stringFoo.Value + stringBar.Value ); // this will succeed, even though you intend stringFoo and stringBar to be incompatible

As operators are part of a class' interface then you must re-implement them - however you can just wrap the contained class' implementation, so you only need to do boring grunt-work to get this to work.

Urano answered 21/6, 2012 at 14:53 Comment(0)
I
0

You could try overloading the implicit conversion operator on your class to a string. Then you you wouldn't have to have update your references to .Value. The classes will have to remain separate and you will have to create constructors for them that accept the string value. Best practice would be to keep the string readonly so that the class becomes immutable. Mimicking the string class it's meant to be a representation of.

public class FirstKind 
{
      private readonly string value;
      public FirstKind(string v) { this.value = v };
      public static implicit operator string(FirstKind v){ return v.value; }
}

public class SecondKind 
{
      private readonly string value;
      public SecondKind (string v) { this.value = v };
      public static implicit operator string(SecondKind v){ return v.value; }
}
Immodest answered 21/6, 2012 at 14:54 Comment(2)
You should use a property rather than exposing a string field, and it should be read-only, not mutable.Infamous
I didn't intend for it to remain exposed. Just display the concept. I would keep it private and read only and then only have it accessible via the implicit conversion. It would make the code more readable in the long run.Immodest
A
0

You can almost use your generic class. Only implicit casting from string (or other type) to the derrived class can never work, because the base class can't know if string should be cast to firstkind or secondkind. So that part should be copied, the rest however can stay in the base class:

private class FirstKind : KindOf<string>
{
    public static implicit operator FirstKind(string value) { return new FirstKind{Value = value}; }
}
private class SecondKind : KindOf<string>
{
    public static implicit operator SecondKind(string value) { return new SecondKind{Value = value}; }
}

Casting to the base class can still be done from within the generic class:

        FirstKind Foo = "Foo"; //via implicit operator in FirstKind
        string stringFoo = Foo; //via implicit operator in baseclass
Autohypnosis answered 21/6, 2012 at 14:55 Comment(2)
The problem that remains, though is that you can't do something like Foo.Length. You'd have to fix all your code to do things like Foo.Value.Length which probably isn't worth all the hassle.Maize
Yep, that in turn would call for an inbetween KindOfString class that inherits from KindOf<string> and implements those properties, from which the other kinds inherit (or if only strings are used anyway, skip the generic class alltogether).Autohypnosis

© 2022 - 2024 — McMap. All rights reserved.