0

chaps/chapettes, I understand there are questions related to this but this is somewhat different - all related questions I could find only used one parameter as an example. Anyways, to the point:

This year, I have converted source code written in Delphi to C#. Beyond this, the scope of my tasks has been to optimize and generally improve the code base. The source code has been written by a handful of individuals, each with no knowledge or experience of software engineering principles or techniques - so some of the code is abismal.

Anyhow, perhaps someone can provide a suggestion/solution to my quarrels:

Currently, in C# have a class for storing 9 values:

class StoreStruct
{
    int A1 { get; set;}
    int B1 { get; set;}
    int C1 { get; set;}

    int A2 { get; set;}
    int B2 { get; set;}
    int C2 { get; set;}

    int A3 { get; set;}
    int B3 { get; set;}
    int C3 { get; set;}
}

Now what I have an issue with is that, ideally, I would like to pass the properties of this class into methods by ref. However, I know I can't do this. Instead the source code works by creating temp local variables, passes these by ref and then assigns the class properties to these values. This can be seen as follows:

private void MethodA()
{
    var temp = new StoreStruct();

    var a1 = 0;
    var b1 = 0;
    var c1 = 0;

    var a2 = 0;
    var b2 = 0;
    var c2 = 0;

    var a3 = 0;
    var b3 = 0;
    var c3 = 0;

    if (expression1)
    {
        MethodB(ref a1, ref b1, ref c1, 1, 1);

        temp.A1 = a1;
        temp.B1 = b1;
        temp.C1 = c1;       
    }

    if (expression2)
    {
        MethodB(ref a2, ref b2, ref c2, 2, 2);

        temp.A2 = a2;
        temp.B2 = b2;
        temp.C2 = c2;   
    }

    if (expression3)
    {
        MethodB(ref a3, ref b3, ref c3, 3, 3);

        temp.A3 = a3;
        temp.B3 = b3;
        temp.C3 = c3;   
    }
}

private void MethodB(ref int a, ref int b, ref int c, int num1, int num2)
{
    a = num1 + num2;
    b = num1 - num2;
    c = num1 * num2;
}

What I would like to do in an ideal world:

MethodB(ref temp.A1, ref temp.B1, ref temp.C1, 1, 1);

From looking at other posts, I understand why this isn't catered for in C# and quite frankly I agree with the reasoning behind it. I have seen a few workarounds and a few suggestions in other posts but these only relate to an example with one method call and only one parameter being passed by ref. Does anyone have an elegant solution that would allow me to update the class properties in MethodB without having to pass temporary variables?

dw1991
  • 51
  • 1
  • 5
  • Can you pass `StoreStruct` into the methods? – Chris Nov 14 '13 at 16:32
  • 7
    Properties are nothing but syntactic sugar for getter and setter function calls, which is why you cannot pass them by reference. In general in C# if you're using ref params, you're doing it wrong. Simply pass the StoreStruct class and let thefunction set the properties. A class is a reference type, so essentially all objects are passed "by reference" by default in C#. – Jonathon Reinhart Nov 14 '13 at 16:32
  • Would it make more sense to make `A`, `B`, and `C` arrays? That might make this far easier to work with. You might simply replace them, or create `IList` properties with a class that just emulates it (e.g. so that `A[1] = 3524` sets `A1`), to at least centralize this confusing logic instead of having it everywhere, but that might be more confusing. – Tim S. Nov 14 '13 at 16:32
  • Just pass the entire StoreStruct object into what ever methods... have the method set the properties – Dave Lawrence Nov 14 '13 at 16:40
  • Perhaps I should have been more clear in the original post. While I could pass the "StoreStruct" object into "MethodB". This would require adding in extra code to MethodB in order to check which properties would be set accordingly. Moreover, "MethodB" in practice is a relatively large method, doing alot of work. This is why I was opting for a solution along the lines of passing the properties by ref. – dw1991 Nov 14 '13 at 16:56

8 Answers8

1

Just remove getters and setters from StoreStruct.

artur grzesiak
  • 20,230
  • 5
  • 46
  • 56
  • I consider this a bad solution. If OP could just make them fields, he/she would have already done so. Also, you change some other behaviour, eg. fields are not polymorphic. – Zong Nov 14 '13 at 16:43
0

Personally I would use the workaround you did in your question. However if you really want it you would need to pass delegates in to the function that would assign the values for you then call them inside your function.

if (expression1)
{
    MethodB((a) => temp1.A1 = a, 
            (b) => temp1.B1 = b, 
            (c) => temp1.C1 = c, 
             1, 1);     
}

private void MethodB(Func<int> setA, 
                     Func<int> setB, 
                     Func<int> setC, 
                     int num1, int num2)
{
    setA(num1 + num2);
    setB(num1 - num2);
    setC(num1 * num2);
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
0

Properties are nothing but syntactic sugar for getter and setter function calls, which is why you cannot pass them by reference. In general in C# if you're using ref parameters, you're probably doing it wrong.

Simply pass the StoreStruct object, and let the function set the properties. A class is a reference type, so essentially all objects are passed "by reference" by default in C#.

I think modifying your StoreStruct will help with this, and eliminate a bunch of madness:

class Thing {
    int A { get; set; }
    int B { get; set; }
    int C { get; set; }
}

class StoreStruct {  //  Not actually a struct!
    public readonly Thing thing1;
    public readonly Thing thing2;
    public readonly Thing thing3;
}

Use:

private void MethodA()
{
    var temp = new StoreStruct();

    if (expression1)
    {
        MethodB(temp.thing1, 1, 1);     
    }

    if (expression2)
    {
        MethodB(temp.thing2, 1, 1);     
    }

    if (expression3)
    {
        MethodB(temp.thing3, 1, 1);     
    }
}

private void MethodB(Thing thing, int num1, int num2)
{
    thing.A = num1 + num2;
    thing.B = num1 - num2;
    thing.C = num1 * num2;
}
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
0

Well, if it were me the first thing I'd do is try to get some real names here. What's an A1, B2, etc? Is it a cash register? A puppy? A space sled? Names should reflect what's going on. Next, ideally classes should modify their own data as much as possible, so I would tend to think in terms of passing the object and calling as few methods as possible on the object to do whatever modifications are needed rather than passing around a set of flags, especially if the latter have really obtuse names. Sorry if that seems like more of a general criticism, but it goes to what you mentioned about being tasked to improve the code base over time. (If they're really structs, I've always found properties to be overkill except insofar as they might aid debugging).

John Lockwood
  • 3,787
  • 29
  • 27
  • The above is just an abstract example with generic names. I can't post the actual code for contractual reasons. – dw1991 Nov 14 '13 at 16:49
0

It seems that you are having three sets of values in your class. If you make a class of such a set, you can have three values in the class:

class ABC {
  int A { get; set; }
  int B { get; set; }
  int C { get; set; }
}

class StoreStruct {

  ABC ABC1 { get; set; }
  ABC ABC2 { get; set; }
  ABC ABC3 { get; set; }

  public StoreStruct {
    ABC1 = new ABC();
    ABC2 = new ABC();
    ABC3 = new ABC();
  }

}

Now you can pass an ABC value into MethodB, and as that is a changeable set of values, you don't even need the ref keyword for the parameter:

private void MethodB(ABC abc, int num1, int num2) {
  abc.A = num1 + num2;
  abc.B = num1 - num2;
  abc.C = num1 * num2;
}

Call:

MethodB(temp.ABC1, 1, 1);

You could also make MethodB a member of the class ABC, so that you don't pass the value to the method, you call the method on the value:

class ABC {
  int A { get; set; }
  int B { get; set; }
  int C { get; set; }

  public void MethodB(int num1, int num2) {
    A = num1 + num2;
    B = num1 - num2;
    C = num1 * num2;
  }
}

Usage:

temp.ABC1.MethodB(1, 1);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
0

I agree with Jonathan's comment that you're probably doing something wrong and could encapsulate your state in a way that doesn't require passing by ref.

In order to do what you want, you can use a backing variable for the getters/setters.

private int _a1;
public int A1
{
  get
  {
    return _a1;
  }
  set
  {
    _a1 = value;
  }
}

public void Foo()
{
  Bar(ref _a1);
}
Adrian
  • 251
  • 1
  • 5
0

It looks like you want to exchange every Property of a class. In the OOP-world the best way to do this is to send the complete class to the method, not just the properties:

public void MethodB(ref StoreStruct store) {
 // store.A1=[...];
}
Ole Albers
  • 8,715
  • 10
  • 73
  • 166
0

Wow, lots of answers to this question already. Well, here's another. You could try creating a class that can act as a reference to an integer like this:

class IntRef
{
   private int value;
   public IntRef(int value)
   {
      this.value = value;
   }

   public static implicit operator IntRef(int value)
   {
      return new IntRef(value);
   }

   public static implicit operator int(IntRef value)
   {
      return value.value;
   }
}

Then change your int declarations and your ref int paremeters to IntRef.

Be aware that if you do this, though, that your set procedure may not run. You may have to move some of that code into the IntRef class or raise an event from IntRef so that the StoreStruct can react to the changed value.

BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146