0

I was given a new project at work, and I am very new to c#.

I have a base class that will be derived many times, and so I am trying to come up with a way to check if a derived class contains a const string value from a .contains() method implemented in the base class. I cannot use a string array because I have to reference the string values by variable name when I am making calls to other methods in the derived class. The reason I want the base class to implement it is this project will be used for many years at my company, and once I pass it off, new hires will be the ones implementing the base class.

I don't want to implement multiple data structures to achieve this, i.e. a string array and an enum, for simplicity sakes.

What I want is something like this:

public abstract class Base
{
   public bool contains(string s)
   { 
       // some implementation here
       // this would return true if SomeDataStructure
       // contains a const string == s
   }
}

public Derived : Base
{
   SomeDataStructure {
       const string = "string 1";
       const string = "string 2";
       const string = "string 3";
   }
}  

Is this possible, and if so, what data structure and implementation would I use?

Thanks!

Jeffrey
  • 3
  • 3
  • You can't without the same method in each derived class. It is better to put the string array in the base class instead of inherited class. – jdweng Aug 01 '15 at 14:59
  • I am unable to use a string array because I need the strings to be referenced by name i.e. `derivedClassInstance.STRING1`. I am unable to put the array in the base class because each derived class is going to have a different set of constants. – Jeffrey Aug 01 '15 at 15:52
  • It sounds like an odd design. Essentially the base class has no knowledge of this data structure which means the contains function should be placed on the derived class (i.e. make it an abstract function on base). This most likely results in code duplication or the use of reflection if you really want it on the base class; you're probably better of rethinking what you're doing. – diepvriezer Aug 01 '15 at 16:09
  • Make the Data Structure a Dictionary or Dictionary. – jdweng Aug 01 '15 at 16:53
  • @jdweng As stated earlier, I must, due to coding standards, use constants to reference using variable name i.e. `derivedClass.dataStructure.SOME_STRING`. I cannot reference the derived class' strings using `dictionaryStruct["string_text"]`. I understand what you're getting at, I just am unable to do it that way. – Jeffrey Aug 01 '15 at 17:06
  • The next step would be an `Expando` – Eris Aug 02 '15 at 02:08

3 Answers3

0

You can use reflection to get all the fields of class
Check their type if it is string
If Yes then check if given string is equal to its value
etc, You can see reflection from msdn

Nauman Mustafa
  • 103
  • 2
  • 9
  • If I use reflection in the base class, what data structure would I use to access only the constants I want to check? There's only a subset of the derived class constants that I want to be checked, and I cannot use an array because I need to reference the strings by name i.e. `derivedClassInstance.STRING1`, or `derivedClassInstance.dataStructure.STRING1`. Thanks! – Jeffrey Aug 01 '15 at 15:55
0

I'm really struggling with what you're trying to achieve, it looks like you're marking classes using string constants but I'm not sure. Just to get this straight, you want the function contains(s) to return true or false depending on the value of the constants declared on the derived class?

You could declare an attribute (lets say MarkAttribute), then tag string constants which should be registered in the derived or base class and use reflection on the base class method as below. It's not really efficient to enumerate all fields on every call, so you might want to consider caching the results.

class Base
{
    [Mark]
    public const string StringOnBase = "!?";

    private static readonly Dictionary<Type, string[]> fieldsPerInstance = new Dictionary<Type, string[]>();

    public bool ContainsMarked(string s)
    {
        var t = this.GetType();
        if (!fieldsPerInstance.ContainsKey(t))
        {
            fieldsPerInstance[t] = t
                .GetFields(BindingFlags.Public)
                .Where(f => f.GetCustomAttribute<MarkAttribute>() != null)
                .Where(f => f.FieldType == typeof(string))
                .Select(f => f.GetValue(this) as string)
                .ToArray();
        }

        return fieldsPerInstance[t].Contains(s);
    }
}

class Derived : Base
{
    [Mark]
    public const string StringA = "A";
    public const string OtherString = "!?";
}

class DerivedAgain : Derived
{
    [Mark]
    public const string StringB = "B";
}

[AttributeUsage(AttributeTargets.Field)]
class MarkAttribute : Attribute
{
}


class Program
{
    static void Main(string[] args)
    {
        var derived = new Derived();
        derived.ContainsMarked(DerivedAgain.StringB); // false
        derived.ContainsMarked(Base.StringOnBase); // true;
    }
}

If you're checking on value however, note that the following code will return true, because OtherString is set to the same value as StringOnBase.

derived.ContainsMarked(Derived.OtherString); // true

Could you give a real world example of where you would want to apply this though?

  • I'm working with an embedded board that has channels that I need to associate with the properties of each class. Imagine a base class vehicle. I need to define the possible channels of each vehicle type (airplane will have much different properties than boat). So each derived class will have separate attributes (the name of the const strings) associated with a channel name (the value of the const string). I need to ensure that the channels in the config are defined in the derived class (using const value) and then be able to access the channels from another class using the const names. – Jeffrey Aug 01 '15 at 17:17
  • I also want to eliminate the need for some intern to keep reimplementing .contains() in the derived classes, thus I want to implement it in the base class. – Jeffrey Aug 01 '15 at 17:19
  • How often do you need to query the channel info? Assuming the channels are known at compile time, you could define them in an enum `Channel` and then use an attribute to tag properties in any class: `[Channel(Channel.One)] public bool SomeProperty { get; set; }` – diepvriezer Aug 01 '15 at 18:30
  • Oops, just saw your edit. Channels are known at compile-time. Like I said, I'm very new to c# (coming from Java). Am I able to access that like `derivedClass.Channel.One`? If so, I think that would work out for me if I could still implement `contains()` in the base class. – Jeffrey Aug 01 '15 at 18:37
  • Ok let's start again, you have some class, derived or not, which exposes properties (public fields are not recommended, but maybe because you're doing embedded that's fine). These properties or fields have a channel associated with them and all channels are known at compile time. Are the channels unique for each (derived) class? And how and where do you intent to use the channel information? – diepvriezer Aug 01 '15 at 19:10
  • I have an `abstract dictionary` defined in the base class. I also have an `add(string, channel)` method defined in the base. Due to coding standards, I need to ensure that only channels associated with a derived class are added to that derived class' dictionary. Thus, before adding a channel, I want to know if the derived class contains a definition for the channel. This all happens at start. During runtime, I need to access the channels from the `dictionary` individually using the derived class' constants (due to coding standards, I cannot access it via a string literal.) – Jeffrey Aug 01 '15 at 19:22
  • So at startup, I would build the `dictionary` in the derived class using something like `if ( contains( channel_id ) { add( channel_id, channel) ;}`, then an independent class would use the channels of the derived class like `channels.GetChannel( derivedClass.CHANNEL1 ).ChangeState( true ) `. So I simply want one data structure to manage the constant channel names, which will be used to ensure the `dictionary` is properly built, as well as to access the individual channels from an external class. Does that make sense? Thanks again for your help! – Jeffrey Aug 01 '15 at 19:29
  • It's starting to make sense, just one thing, are channels classes themselves, considering you have a dictionary? So would you have a `ChannelOne` class, a `ChannelTwo` class perhaps sharing a ChannelBase, or maybe just one common Channel class providing the `ChangeState` method? – diepvriezer Aug 01 '15 at 20:14
  • All channels are of one common class type. The channel class has 3 properties (board, port, bit), and an event that is raised when the channel's state changes on board. – Jeffrey Aug 02 '15 at 00:12
  • Assuming the references to all channels are held somewhere else, couldn't you just expose properties for each channel on the derived class? So an airplane may have four channels, exposed as properties `ChannelTrim`, `ChannelThrottle`, etc, which are set in the constructor of the derived class `this.ChannelTrim = Manager.GetChannel(1)` instead of adding them to a dictionary. If you need a function which enumerates all channels, you can use reflection. – diepvriezer Aug 02 '15 at 00:32
  • I would still need to manage a set of constant strings to validate the channels in the configuration file. That's where my whole question came in: I didn't know how to manage a data structure of constant strings such that I could both validate the channels in the config file, as per coding standards, and address each channel using the constant string's name, i.e. `channels.get(CHANNELNAME_STRING)`. – Jeffrey Aug 02 '15 at 00:52
0

Ok so from the discussion on the previous answer, would something like this suffice?

abstract class Base
{
    protected Base()
    {
        var t = GetType();
        if (!channelMap.ContainsKey(t))
            channelMap[t] = GetDeclaredChannels(t);

        channels = channelMap[t];
    }

    // All derived children must declare channel constants in a (static) nested class.
    private const string ChannelClassName = "Channel";

    // Static cache which maps types to an array of declared channels.
    private static readonly Dictionary<Type, string[]> channelMap = new Dictionary<Type, string[]>();
    private readonly string[] channels;

    // Exposed read-only list of channels.
    public IReadOnlyCollection<string> Channels
    {
        get { return channels; }
    }

    public bool Contains(string s)
    {
        return channels != null && channels.Contains(s);
    }

    private static string[] GetDeclaredChannels(Type type)
    {
        var channelType = type.GetNestedType(ChannelClassName);
        if (channelType == null)
            return null; // no channels declared.

        return channelType
            .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
            .Where(f => f.IsLiteral && !f.IsInitOnly)
            .Select(f => f.GetValue(null) as string)
            .ToArray();
    }
}

class Derived : Base
{
    public class Channel // may be static or not
    {
        public const string One = "test";
        public const string Two = "test2";
    }
}
class Derived2 : Base { }


new Derived().Contains(Derived.Channels.ChannelOne); // true;
new Derived2().Contains(Derived.Channels.ChannelOne); // false

Less extensible than before, as now all string constants declared in a nested type are marked as channels. You may also run into problems when a derived class is derived again, it needs to declare additional constants with public new class Channel : Derived.Channel.