2

I am building internal logic for a game in C# and coming from C++ this is something that might be lost in translation for me.

I have an object, Ability that calculates the bonus it provides and returns that as an integer value. The calculation is meant to be dynamic and can change depending on a variety of variables.

    public class Ability: Buffable
    {

        public string abbr { get; private set; }

        public Ability(string name, string abbr, uint score) : base(name, score)
        {
            this.abbr = abbr;
        }

        // Ability Modifier
        // returns the ability modifier for the class.
        public int Ability_modifier()
        {
            const double ARBITARY_MINUS_TEN = -10;
            const double HALVE = 2;
            double value = (double)this.Evaluate();
            double result = (value + ARBITARY_MINUS_TEN) / HALVE;

            // Round down in case of odd negative modifier
            if (result < 0 && ((value % 2) != 0))
            {
                result--;
            }
            return (int)result;
        }

I then have another object, Skill which should be aware of that bonus and add it into it's calculation. I wanted to pass an Ability into the constructor of Skill by reference and then store that reference so that if the Ability changed the calculation would as well. The obvious problem with this being that apparently storing references is taboo in C#.

Is there either a work around way to do this or an alternate way to approach this problem that my pointer infested mind isn't considering? I would greatly prefer not to have to pass the ability to the function that evaluates Skill every time, since the one referenced never changes after construction.

stygma
  • 523
  • 4
  • 19

3 Answers3

3

The obvious problem with this being that apparently storing references is taboo in C#.

Absolutely not. References are stored all over the place. You're doing it here, for example:

this.abbr = abbr;

System.String is a class, and therefore a reference type. And so the value of abbr is a reference.

I strongly suspect you've misunderstood how reference types work in C#. If you remember a reference to an object, then changes to the object will be visible via the reference. However, changes to the original expression you copied won't be.

For example, using StringBuilder as a handy mutable reference type:

StringBuilder x = new StringBuilder("abc");

// Copy the reference...
StringBuilder y = x;

// This changes data within the object that x's value refers to
x.Append("def");

// This changes the value of x to refer to a different StringBuilder
x = new StringBuilder("ghi");

Console.WriteLine(y); // abcdef

See my articles on references and values, and parameter passing in C# for much more detail.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks, I definitely was misunderstanding how that worked. The articles were quite helpful. – stygma Mar 23 '13 at 18:04
0

I am not quite seing enough of your code to give a concrete example, but the way to do this is to pass in a lambda delegate such as () => object.property instead of this: object.property.

Pieter Geerkens
  • 11,775
  • 2
  • 32
  • 52
  • I think you are answering a more advanced question than that being posed by the OP. Eric provides a generic implementation of your proposal [here](http://stackoverflow.com/a/4543089/18192). – Brian Mar 23 '13 at 02:33
  • @Brian: I know that post well; I have stumbled across it a few times over the past months. However, my reading of the post is that OP wants to pass in a means of recalculating the property, rather than the current value of the property; my proposal is a means of doing that. – Pieter Geerkens Mar 23 '13 at 03:00
-1

In C#, there are reference types and value types. All non-value-type objects are passed by reference, so there should be no issue with references. Just pass it, and it will be passed by reference.

J.T. Taylor
  • 4,147
  • 1
  • 23
  • 23
  • 4
    No, objects aren't passed by reference - references are passed by value (by default). For `ref` parameters, there's *real* pass by reference, which can be applied to parameters of either reference types or value types. – Jon Skeet Mar 22 '13 at 22:46
  • It depends on your definition of the term "pass by reference", which is reasonably debatable. One useful definition I've seen often is that "if a function modifies a value, when passing by reference the modifications appear also within the scope of the calling function, which they do not when passing by value." By this definition, any C# "reference type" would be "passed by reference", as opposed to "by value". Passing a "reference by value", in C++, would be using a pointer, which to a C++ developer, equals "passing by reference". – J.T. Taylor Mar 25 '13 at 19:49
  • No, it's in terms of changing the value of the parameter. A change to the parameter itself (e.g. `builder = new StringBuilder()` ) won't be visible to the caller. A change to the data within the object that the parameter value refers to (e.g. `builder.Append("Foo")`) will be seen, but that does *not* change the value of the parameter itself. The value of the parameter is just a reference, and the reference will be the same afterwards. I really don't think there's any debate here - the language specification is pretty clear. – Jon Skeet Mar 25 '13 at 19:55
  • In particular, see sections 10.6.1.1 and 10.6.1.2 of the C# 4 specification - "value parameters" and "reference parameters". Under "value parameters": "A method is permitted to assign new values to a value parameter. Such assignments affecet only the local storage location represented by the value parameter; they have no effect on the actual argument given in the method invocation." That's regardless of whether the type of the parameter is a reference type or a value type. Please read my article on the topic too: http://pobox.com/~skeet/csharp/parameters.html – Jon Skeet Mar 25 '13 at 19:57
  • I clearly see your point, thanks for the explanation. Would you agree that re-assigning parameters 'builder = new StringBuilder() within the called method would be an edge case, maybe used only for the most extreme memory-constrained applications where recycling a single reference could have benefit? If that's true, I wonder if this wouldn't be a candidate for a compiler warning. – J.T. Taylor Mar 25 '13 at 21:08
  • 1
    While it's relatively unusual to change the value of a parameter, it's far from unheard of. For example, in order to avoid nullity checks throughout the rest of a method (but to allow a null reference as input) you might well start a method with: `text = text ?? "";`. Note that this isn't recycling a single reference - it's recycling a single *variable* (the parameter). – Jon Skeet Mar 25 '13 at 21:16