2

My question has sort of already been asked here: How to build a group of constant variables in c# But I feel as though it is not quite asking the same thing. I will try to explain, please hear me out.

Suppose for a moment that you had to represent attributes for a player, such as Strength, Agility, Stamina, etc. But at the same time you also wanted to list other attributes which were not directly related to the same group. Aside from Primary Attributes you could have Probabilistic Attributes, such as Accuracy, Evasion, and Critical Strike. As well as Logistical Attributes, like: Movement, Initiative, and Jump. Naturally, these Attributes serve different purposes, and thus, should be grouped seperately(Or at least in my mind they should). I could implement this using multiple classes:

namespace Example
{
    public class PrimaryAttributes
    {

        public int Strength { get; set; }
        public int Agility { get; set; }
        public int Stamina { get; set; }
        public int Intellect { get; set; }
        public int Willpower { get; set; }
        public int Spirit { get; set; }

        public PrimaryAttributes()
        {
            // Do stuff here
        }

        // Do more stuff here


    }
}

Continued...

namespace Example
{
    public class ProbabilisticAttributes
    {

        public int Accuracy { get; set; }
        public int Evasion { get; set; }
        public int CriticalStrike { get; set; }

        public ProbabilisticAttributes()
        {
            // Do stuff here
        }

        // Do more stuff here


    }
}

Continued...

namespace Example
{
    public class LogisticalAttributes
    {

        public int Movement { get; set; }
        public int Initiative { get; set; }
        public int Jump { get; set; }

        public LogisticalAttributes()
        {
            // Do stuff here
        }

        // Do more stuff here


    }
}

And then host them all in a single class, like this:

namespace Example
{
    public class Statistics
    {

        public PrimaryAttributes PrimaryAttributes { get; set; }
        public ProbabilisticAttributes ProbabilisticAttributes { get; set; }
        public LogisticalAttributes LogisticalAttributes { get; set; }

        public LogisticalAttributes()
        {
            PrimaryAttributes = new PrimaryAttributes();
            ProbabilisticAttributes = new ProbabilisticAttributes();
            LogisticalAttributes = new LogisticalAttributes();
        }

        // Do more stuff here


    }
}

That would achieve the effect of being able to call something along the lines of "stats.PrimaryAttributes.Strength", which is clean and organized. However, I would prefer to not do this. I would like to host all the variables within the same class without having to use other classes. So what's the catch? I still want to be able to organize them behind an extended scope, or I guess what could be called a namespace; a sort of divider, if you will. I don't think anything like this exists within the C# language...or any language for that matter. But I want to know how close I can get to replicating this behavior. Here is an example of what I am looking for (not real C#, just an example).

namespace Example
{
    public class Attributes
    {
        group PrimaryAttributes // Imaginary "group" keyword.
        {
            int Strength { get; set; }
            int Agility { get; set; }
            int Stamina { get; set; }
            int Intellect { get; set; }
            int Wisdom { get; set; }
            int Spirit { get; set; }
        }

        group ProbabilisticAttributes // Imaginary "group" keyword.
        {
            int Evasion { get; set; }
            int Accuracy { get; set; }
            int CriticalStrike { get; set; }
        }

        group LogisticalAttributes // Imaginary "group" keyword.
        {
            int Movement { get; set; }
            int Initiative { get; set; }
            int Jump { get; set; }
        }

        public Attributes()
        {
            // Don't have to declare or initialize anything.
        }

        public int ReturnSomethingAmazing()
        {
            return this.PrimaryAttributes.Strength * this.PrimaryAttributes.Agility; // Notice the group accessor usage.
        }

        // Do more stuff here


    }
}

I hope you can see what I am talking about now. I want to be able to "scope" variables, but without the use of separate classes. I have also taken the liberty of thinking about how this could be parsed at compile time. Essentially, the group scopes would just be inlined, so myClassInstance.PrimaryAttributes.Strength

, would be minimalized to myClassInstance.Strength. The scope doesn't actually exist, it's only there for the programmer to better facilitate organization, without expending more objects in memory at runtime.

To end this, I wanted to summarize two questions.

Is this in any way possible using current C#?

and

Do you think this would be a good suggestion to recommend to Microsoft for an addition to the C# programming language?

Community
  • 1
  • 1
Krythic
  • 4,184
  • 5
  • 26
  • 67
  • Using regions inside the class wouldn't make the trick? Or you could use partial classes and use a different file for each group. – Andrew Mar 25 '17 at 03:58
  • 3
    There is nothing wrong with the `class` approach to organize them. You may prefer a `struct`, it's a little more verbose as to their purpose of being property bags. I think you're overthinking it, as a `class` for each category provides exactly what you need. – mariocatch Mar 25 '17 at 03:58
  • @Andrew But Regions can't be accessed by the name of the region. – Krythic Mar 25 '17 at 03:59
  • @Krythic Yes I understand, and I would not be concerned with heap allocations of a handful of properties. If they become the bottleneck for performance of your app then I'll eat my shoe :) Again, I think you're overcomplicating something that has an established pattern already. – mariocatch Mar 25 '17 at 04:04
  • @mariocatch I shouldnt have mentioned the memory because it sidesteps my overall point. I was merely trying to clarify a possible usage. My concern is organization. I couldn't give two ***** about the memory, I just want the organization. – Krythic Mar 25 '17 at 04:05
  • @Krythic Then your only option is to use a class/struct to logically group them. – mariocatch Mar 25 '17 at 04:06
  • I'm not clear on exactly what you mean my organization. If you mean for reading the code then regions should work. If you mean for when you write code against this class and want intellisense to help you pick the right type then you could just prefix the names of the properties. Another organization option might be using partial classes. But really this feels like a opinion based question that doesn't work well on this site. – juharr Mar 25 '17 at 04:13
  • @juharr My question is "Is this possible with current C#?" and the only opinion based part of it is "Do you think I should recommend this to Microsoft?" – Krythic Mar 25 '17 at 04:15

2 Answers2

1

This is a way I just came up with, not sure if it's worth it but you may like it:

public class Attributes
{
    // PrimaryAttributes
    public class PrimaryAttr
    {
        public int Strength { get; set; }
        public int Agility { get; set; }
        public int Stamina { get; set; }
        public int Intellect { get; set; }
        public int Wisdom { get; set; }
        public int Spirit { get; set; }
    }

    private PrimaryAttr _primaryAttr;
    public PrimaryAttr PrimaryAttributes
    {
        get
        {
            if (this._primaryAttr == null)
                this._primaryAttr = new PrimaryAttr();

            return this._primaryAttr;
        }
    }


    // ProbabilisticAttributes
    public class ProbabilisticAttr
    {
        public int Evasion { get; set; }
        public int Accuracy { get; set; }
        public int CriticalStrike { get; set; }
    }

    private ProbabilisticAttr _probabilisticAttr;
    public ProbabilisticAttr ProbabilisticAttributes
    {
        get
        {
            if (this._probabilisticAttr == null)
                this._probabilisticAttr = new ProbabilisticAttr();

            return this._probabilisticAttr;
        }
    }


    // LogisticalAttributes
    public class LogisticalAttr
    {
        public int Movement { get; set; }
        public int Initiative { get; set; }
        public int Jump { get; set; }
    }

    private LogisticalAttr _logisticalAttr;
    public LogisticalAttr LogisticalAttributes
    {
        get
        {
            if (this._logisticalAttr == null)
                this._logisticalAttr = new LogisticalAttr();

            return this._logisticalAttr;
        }
    }


    // Rest of the implementation    
    public Attributes()
    {
        // Don't have to declare or initialize anything.
    }

    public int ReturnSomethingAmazing()
    {
        return this.PrimaryAttributes.Strength * this.PrimaryAttributes.Agility; // Notice the group accessor usage.
    }

    // Do more stuff here


}
Andrew
  • 7,602
  • 2
  • 34
  • 42
  • As others have mentioned, mutable structs would be a better choice. I upvoted, simply because you're giving me your time and consideration. – Krythic Mar 25 '17 at 04:17
  • I'm not sure that's a good idea. http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – Andrew Mar 25 '17 at 04:18
1

With this kind of questions, the only answer possible is an expression of an opinion. Sometimes people think differently, sometimes they don't, so please bear with me.

OOP gets hard and confusing sometimes but here is how I think about it and what I would've done. Also, I understand that you want to do it without "separate classes" and what I will suggest is exactly the opposite. But I believe doing it using separate abstracts is where you "find The Force, Luke".

First, let's create an enum to simplify the grouping (and de-grouping when we access the properties in the future):

public enum AttributeCategory
{
    Primary,
    Probabilistic,
    Logistical
}

Now, the concept of an attribute is a very good candidate for an interface. Let's create it and create an associated abstract to reduce and simplify the code (yeah, excessive use of interfaces, but who cares :D ):

public interface IAttribute
{
    string AttributeName { get; }
    AttributeCategory Category { get; }
    int Value { get; }
}

public abstract class AttributeBase : IAttribute
{
    protected virtual string AttributeName { get; }
    protected virtual AttributeCategory Category { get; }
    protected virtual int Value { get; }
}

// Note: one might want to implement a generic type for value, something like AttributeBase<T> {... T Value { get; } }

Now let's define a couple of additional abstracts, you'll see why I prefer doing it this way a tiny bit later:

public abstract class PrimaryAttribute : AttributeBase
{
    protected override sealed AttributeCategory Category { get; } = AttributeCategory.Primary;
}

public abstract class ProbabilisticAttribute : AttributeBase
{
    protected override sealed AttributeCategory Category { get; } = AttributeCategory.Probabilistic;
}

public abstract class LogisticalAttribute : AttributeBase
{
    protected override sealed AttributeCategory Category { get; } = AttributeCategory.Logistical;
}

OK, now we can implement actual attributes:

public sealed class Strength : PrimaryAttribute
{
    protected override string AttributeName => "Strength";
}

public sealed class Stamina : PrimaryAttribute
{
    protected override string AttributeName => "Stamina";
}

public sealed class Accuracy : ProbabilisticAttribute
{
    protected override string AttributeName => "Accuracy";
}

public sealed class Initiative : LogisticalAttribute
{
    protected override string AttributeName => "Initiative";
}

With the "mess" I've described above we now can create a class that describes the collection of these attributes:

public abstract class AttributeCollectionBase
{
    protected virtual List<IAttribute> Attributes { get; } = new List<IAttribute>();
    protected virtual IEnumerable<IAttribute> Get(AttributeCategory category)
    {
        return Attributes.Where(a => a.Category == category);
    }

    protected AttributeCollectionBase()
    {
        // Yup, we'll need this one place with the constructor that lists the "default" attributes... Might be for the best, though.
        Attributes.Add(new Strength());
        Attributes.Add(new Stamina());
        Attributes.Add(new Accuracy());
        Attributes.Add(new Initiative());
    }
}

Finally, we can create an implementation of this attribute collection:

public sealed class AttributeCollection : AttributeCollectionBase
{
    //... We don't need anything here to start. Maybe add something specific.
}

public class SomeCharacter
{
    //...
    public AttributeCollection Attributes { get; }
}

var someCharacter = new SomeCharacter();

Now, why go through all this pain? Well, now if anything needs to be implemented for, let's say, probabilistic attributes (some generic calculation) - one modification needed, in the abstract class (new virtual method). Any change needed for all the attributes? One piece of code to adjust - AttributeBase. Ever need to separate some default attributes (let's say for a different class of characters) - just another AttributeCollectionBase implementation is needed, for example, KnightAttributeCollection. And, finally, you achieve the "get the attribute by type" implementation like this:

someCharacter.Attributes.Get(AttributeCategory.Logistical);

Nevertheless, this is my personal way I'm sharing and it might seem/be sub-optimal or less than understandable and it doesn't mean anything more than the fact that "it always depends".

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Philip P.
  • 1,162
  • 1
  • 6
  • 15
  • It's interesting but I wonder if that isn't *overarchitecturing* a bit for something as simple as a class with some properties. And you end up having one class per attribute (plus all the supporting ones)! Remember that OP (Krythic) asked "without having to use other classes". :D Also, what if one of them is not of type `int`? Finally, won't that code give you errors since the interface members are not public in your classes? At least this doesn't compile on my local. – Andrew Mar 25 '17 at 18:46
  • @Andrew, If one of them is not int - I've addressed that in one of the code comments, quote: "// Note: one might want to implement a generic type for value, something like AttributeBase {... T Value { get; } }". I have not actually tried the code, you are right, those should be public. Finally, over-architecting - maybe, I'd even say probably. But like it is in software - everything is over-architecting until it isn't. Dealing with over-architecting consequences is much much easier than with under-architecting. – Philip P. Mar 26 '17 at 22:40