174

I have this API function:

public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d, 
     string e, string f, out Guid code)

I don't like it. Because parameter order becomes unnecessarily significant. It becomes harder to add new fields. It's harder to see what's being passed around. It's harder to refactor method into smaller parts because it creates another overhead of passing all the parameters in sub functions. Code is harder to read.

I came up with the most obvious idea: have an object encapsulating the data and pass it around instead of passing each parameter one by one. Here is what I came up with:

public class DoSomeActionParameters
{
    public string A;
    public string B;
    public DateTime C;
    public OtherEnum D;
    public string E;
    public string F;        
}

That reduced my API declaration to:

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)

Nice. Looks very innocent but we actually introduced a huge change: we introduced mutability. Because what we previously had been doing was actually to pass an anonymous immutable object: function parameters on stack. Now we created a new class which is very mutable. We created the ability to manipulate the state of the caller. That sucks. Now I want my object immutable, what do I do?

public class DoSomeActionParameters
{
    public string A { get; private set; }
    public string B { get; private set; }
    public DateTime C { get; private set; }
    public OtherEnum D { get; private set; }
    public string E { get; private set; }
    public string F { get; private set; }        

    public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d, 
     string e, string f)
    {
        this.A = a;
        this.B = b;
        // ... tears erased the text here
    }
}

As you can see I actually re-created my original problem: too many parameters. It's obvious that that's not the way to go. What am I going to do? The last option to achieve such immutability is to use a "readonly" struct like this:

public struct DoSomeActionParameters
{
    public readonly string A;
    public readonly string B;
    public readonly DateTime C;
    public readonly OtherEnum D;
    public readonly string E;
    public readonly string F;        
}

That allows us to avoid constructors with too many parameters and achieve immutability. Actually it fixes all the problems (parameter ordering etc). Yet:

That's when I got confused and decided to write this question: What's the most straightforward way in C# to avoid "too many parameters" problem without introducing mutability? Is it possible to use a readonly struct for that purpose and yet not have a bad API design?

CLARIFICATIONS:

  • Please assume there is no violation of single responsibiltiy principle. In my original case the function just writes given parameters to a single DB record.
  • I'm not seeking a specific solution to the given function. I'm seeking a generalized approach to such problems. I'm specifically interested in solving "too many parameters" problem without introducing mutability or a terrible design.

UPDATE

The answers provided here have different advantages/disadvantages. Therefore I'd like to convert this to a community wiki. I think each answer with code sample and Pros/Cons would make a good guide for similar problems in the future. I'm now trying to find out how to do it.

Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
  • Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin and Martin Fowler's Refactoring book covers this a bit – Ian Ringrose Jun 04 '11 at 21:32
  • 1
    Isn't that Builder-solution redundant if you use C# 4 where you have [optional parameters](http://msdn.microsoft.com/en-us/library/dd264739.aspx)? – khellang Jun 04 '11 at 22:06
  • 1
    I might be stupid, but I fail to see how this is a problem, considering that the `DoSomeActionParameters` is a throwaway object, which will be discarded after the method call. – Vilx- Jun 05 '11 at 01:05
  • @Vilx: I prefer the language/framework to put the barriers around developers in the team whenever possible rather than to see one less cup of coffee create bugs in our project. :) That's an API project so we want to be more careful about what we introduce. Today's throwaway object might get a longer lifetime tomorrow and cause all the problems. Introducing more state with unnecessary mutability is an invitation to hard to discover bugs like there isn't enough already :) – Sedat Kapanoglu Jun 05 '11 at 10:44
  • @Ian: I'm not really sure the book covers the specific problem with unwanted and unnecessary mutability introduced. How can I be sure? – Sedat Kapanoglu Jun 05 '11 at 10:45
  • @khellang: The problem is not about optional parameters. But if you meant "named parameters", there is an answer mentioning that with my comment added. – Sedat Kapanoglu Jun 05 '11 at 10:46
  • 6
    Note that I'm not saying that you should avoid readonly fields in a struct; immutable structs are a best practice and readonly fields help you create self-documenting immutable structs. My point is that **you should not rely upon readonly fields being observed to never change**, because that is not a guarantee that a readonly field gives you in a struct. This is a specific case of the more general advice that you should not treat value types as though they are reference types; they are a very different animal. – Eric Lippert Jun 07 '11 at 06:18
  • @Eric thanks for clarification. Even `readonly` may not be needed for what I'm trying to do since parameter passing with structs imply copying hence mutating them won't cause harm. It will only create a false sense of mutability, similar to modifying integer parameters in a function. I would however LOVE a solution where it was possible to instantiate an immutable class with inline property initialization in C# without using ad-hoc patterns. – Sedat Kapanoglu Jun 07 '11 at 14:08
  • 7
    @ssg: I'd love that too. We have added features to C# that promote immutability (like LINQ) at the same time as we've added features that promote mutability (like object initializers.) It would be nice to have a better syntax to promote immutable types. We're thinking hard about it and have some interesting ideas, but I wouldn't expect any such thing for the next version. – Eric Lippert Jun 07 '11 at 15:00
  • @ssg, how do you initialize your “readonly” struct without using a constructor? – svick Jun 07 '11 at 22:20
  • @svick: `readonly` fields can only be set during initialization such as: `new MyStruct { Prop1 = Value1, Prop2 = Value2 };`. That relieves you from strict parameter ordering. But it's not applicable to properties unfortunately, only fields. – Sedat Kapanoglu Jun 08 '11 at 07:55
  • @ssg, that doesn't seem to work: https://ideone.com/1rjZ7. – svick Jun 08 '11 at 12:45
  • @svick: Then it's not an option. Check out @Jeffrey's mutable struct suggestion in the answers below which still provides isolation AND free parameter order. – Sedat Kapanoglu Jun 08 '11 at 13:42
  • // ... tears erased the text here – Yiğit Yener Dec 07 '15 at 14:14

13 Answers13

93

Use a combination of builder and domain-specific-language style API--Fluent Interface. The API is a little more verbose but with intellisense it's very quick to type out and easy to understand.

public class Param
{
        public string A { get; private set; }
        public string B { get; private set; }
        public string C { get; private set; }


  public class Builder
  {
        private string a;
        private string b;
        private string c;

        public Builder WithA(string value)
        {
              a = value;
              return this;
        }

        public Builder WithB(string value)
        {
              b = value;
              return this;
        }

        public Builder WithC(string value)
        {
              c = value;
              return this;
        }

        public Param Build()
        {
              return new Param { A = a, B = b, C = c };
        }
  }


  DoSomeAction(new Param.Builder()
        .WithA("a")
        .WithB("b")
        .WithC("c")
        .Build());
pim
  • 12,019
  • 6
  • 66
  • 69
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • +1 -- this kind of approach (which I've commonly seen called a "fluent interface") is exactly what I had in mind as well. – Daniel Pryden Jun 04 '11 at 23:57
  • +1 -- this is what I ended up in the same situation. Except that there was just one class, and `DoSomeAction` was a method of it. – Vilx- Jun 05 '11 at 00:54
  • +1 I too thought of this, but since I am new to fluent interfaces, I didn't know if it was a perfect fit here. Thanks for validating my own intuition. – Matt Jun 05 '11 at 22:31
  • I hadn't heard of Builder pattern before I asked this question so this has been an insightful experience for me. I wonder how common it is? Because any developer who hasn't heard of the pattern might have trouble understanding the usage without a documentation. – Sedat Kapanoglu Jun 07 '11 at 09:08
  • 1
    @ssg, it is common in these scenarios. The BCL has connection string builder classes, for example. – Samuel Neff Jun 07 '11 at 11:00
  • Thanks folks. I accepted this answer but I think there are other valid alternatives and one should consider each with their pros & cons. I made the question a community wiki please feel free to add relevant information. – Sedat Kapanoglu Jun 12 '11 at 09:33
  • 1
    This approach looks good but it seems you can't enforce the caller to specify all parameters, while with constructor you can. e.g. you can skip `WithC(string)` even C is required. – joe Apr 22 '21 at 09:25
27

One style embraced in the frameworks is usually like grouping related parameters into related classes (but yet again problematic with mutability):

var request = new HttpWebRequest(a, b);
var service = new RestService(request, c, d, e);
var client = new RestClient(service, f, g);
var resource = client.RequestRestResource(); // O params after 3 objects
Teoman Soygul
  • 25,584
  • 6
  • 69
  • 80
  • Condensing all the strings into a string array only makes sense if they're all related. From the order of arguments, it looks like they're not all completely related. – icktoofay Jun 04 '11 at 21:16
  • Agreed, besides, that would only work if you have a lot of the same types as parameter. – Timo Willemsen Jun 04 '11 at 21:19
  • How is that different than my first example in the question? – Sedat Kapanoglu Jun 04 '11 at 21:54
  • Well this is the usual embraced style even in the .NET Framework, indicating that your first example is quite right in what it's doing even it introduces minor mutability issues (though this example is from another library obviously). Nice question by the way. – Teoman Soygul Jun 04 '11 at 21:56
  • 2
    I have been converging to this style more over time. Apparently significant amount of the "too many parameters" issues can be resolved with good logical groups and abstractions. In the end it makes the code more readable and more modularized. – Sedat Kapanoglu Apr 23 '13 at 15:01
13

Just change your parameter data structure from a class to a struct and you’re good to go.

public struct DoSomeActionParameters 
{
   public string A;
   public string B;
   public DateTime C;
   public OtherEnum D;
   public string E;
   public string F;
}

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code) 

The method will now get its own copy of the structure. Changes made to the argument variable cannot be observed by the method, and changes the method makes to the variable can not be observed by the caller. Isolation is achieved without immutability.

Pros:

  • Easiest to implement
  • Least change of behavior in underlying mechanics

Cons:

  • Immutability is not obvious, requires developer attention.
  • Unnecessary copying to maintain immutability
  • Occupies stack space
Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
  • +1 That's true but doesn't solve the part about "exposing fields directly is evil". I'm not sure how bad things could get worse if I opt for using fields instead of properties on that API. – Sedat Kapanoglu Jun 07 '11 at 14:13
  • 1
    @ssg - So make them public properties instead of fields. If you treat this as a struct that will never have code, then it doesn't make a lot of difference whether you use properties or fields. If you decide to ever give it code (such as validation or something), then you will definitely want to make them properties. At least with public fields, nobody will ever have any illusions about invariants existing on this strucuture. It will have to be validated by the method exactly like parameters would have been. – Jeffrey L Whitledge Jun 07 '11 at 14:23
  • 10
    I think that the exposing-fields-is-evil rule applies to objects in an object-oriented design. The struct that I am proposing is just a bare-metal container of parameters. Since your instincts were to go with something immutable, I think that such a basic container might be appropriate for this occasion. – Jeffrey L Whitledge Jun 07 '11 at 14:28
  • Well even if there is a problem, all disadvantages could be avoided with a simple replace operation as you said. I'll give this a shot and see how it lays out. – Sedat Kapanoglu Jun 07 '11 at 14:45
  • 2
    @JeffreyLWhitledge: I really dislike the idea that exposed-field structs are evil. Such a claim strikes me as being equivalent to saying that screwdrivers are evil and people should use hammers because the points of screwdrivers dent nail heads. If one needs to drive a nail, one should use a hammer, but if one needs to drive a screw, one should use a screwdriver. There are many situations where an exposed-field struct is precisely the right tool for the job. Incidentally, there are far fewer where a struct with get/set properties is really the right tool (in most cases where... – supercat Sep 25 '12 at 22:21
  • ...such a struct would be a good tool, an exposed-field struct would be an even better one). – supercat Sep 25 '12 at 22:22
  • Incidentally, using the struct will require about the same amount of stack space and the same amount of data copying as passing parameters individually, but the former offers the possibility of replacing many discrete load and store operations with a loop. Structures really are the perfect tool for this job. They'd be even better if there were some way of specifying that within a class, a certain pseudo-member name should be interpreted as an alias for a member of an encapsulated field. Then a class that wanted to initialize itself from a struct could simply copy the struct to a field. – supercat Sep 25 '12 at 22:36
11

What you have there is a pretty sure indication that the class in question is violating the Single Responsibility Principle because it has too many dependencies. Look for ways to refactor those dependencies into clusters of Facade Dependencies.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Mark, most of the time it's a design flaw. But for example, what if you try to implement a mathematical function who's input is a lot of paramaters, how would you deal with it? – Timo Willemsen Jun 04 '11 at 21:26
  • Please consider my question as the case where there is no SRP violation. In my original problem, I'm just creating a record in the DB with given parameters. Would that still count? – Sedat Kapanoglu Jun 04 '11 at 21:31
  • 1
    I'd still refactor by introducing one or more parameter objects, but obviously, if you move *all* arguments to a single immutable type, you've accomplished nothing. The trick is to look for clusters of arguments that are more closely related, and then refactor each of those clusters to a separate parameter object. – Mark Seemann Jun 04 '11 at 21:33
  • @Mark: Even if I can group some of them, mutability/immutability problem applies for those sub groups. The question still remains :) – Sedat Kapanoglu Jun 04 '11 at 21:37
  • 2
    You can turn each group into an immutable object in itself. Each object would only need to take a few parameters, so while the actual number of arguments remains the same, the number of arguments used in any single constructor will be reduced. – Mark Seemann Jun 04 '11 at 22:49
  • Oh that's right. But in my case this is the smallest possible unit (fields in a DB table record). But that's a perfect suggestion for cases where such grouping is possible. – Sedat Kapanoglu Jun 05 '11 at 10:22
  • In that case it's just a symptom that the table itself is poorly factored. Even so, keep in mind that your API needs not perfectly reflect the database design. In fact, if it does, you're guaranteed to end up with code which is anything but object-oriented. – Mark Seemann Jun 05 '11 at 11:04
  • 1
    @Mark: I understand that you're trying to identify a deeper problem that caused all that but it's not the case of "poorly factored tables" or "reflecting physical table structure on the public interface" either. It is very possible to get into this situation with a good design. You can take the question as "how can I achieve immutable objects in C# without having constructors with too many parameters?" if it helps. – Sedat Kapanoglu Jun 06 '11 at 08:02
  • 2
    +1 @ssg: Every time I've ever managed to convince myself of something like this, I've proved myself wrong over time by driving useful abstraction out of big methods requiring this level of parameters. The DDD book by Evans may give you some ideas on how to think about this (though your system sounds like it's potentially a far from relevant place to the application of such patters) - (and it's just a great book either way). – Ruben Bartelink Jun 07 '11 at 08:27
  • 3
    @Ruben: No sane book would say "in a good OO design a class should not have more than 5 properties". Classes are grouped logically and that kind of contextualization cannot be based on quantities. However C#'s immutability support starts creating problems at a certain number of properties **before** we start violating good OO design principles. – Sedat Kapanoglu Jun 07 '11 at 08:59
  • @ssg: Calm down. The point is that some abstractions **can be** buried very deep. If you look closely at the reason why a single thing is responsible for understanding and dealing with the intricacies of every single parameter, sometimes one might step back and say "hey, we should unify the concepts of X and Y which means params a,b,c and d really need to be a YXEr args (which has a factory as c and d are not always critical)". Anyway, continue your pooh-poohing. (I was trying to express a _slightly_ different perspective to @Mark but this answer remains the only one with my upvote) – Ruben Bartelink Jun 07 '11 at 11:33
  • @ssg: Actually, I had also upvoted @Teoman Soygul's answer (and your question, but only just now) – Ruben Bartelink Jun 07 '11 at 11:35
  • 3
    @Ruben: I didn't judge your knowledge, attitude or temper. I expect you to do the same. I'm not saying your recommendations are not good. I'm saying that my problem can appear even on the most perfect design and that seems to be an overlooked point here. Although I understand why experienced people ask fundamental questions about the most common mistakes, it gets less enjoyable to clarify it after a couple of rounds. I must again say that it's very possible to have that problem with a perfect design. And thanks for the upvote! – Sedat Kapanoglu Jun 07 '11 at 13:39
  • @ssg: Neither did I - you're arguing things very sanely and showing plenty insight. My point remains that while you're arguing very consistently, the premise is wrong. And I still highly recommend the DDD book even if it has a DDDitis danger in it. The other one which teach you solid like no other is the Micah Martin's ADPPTiC#. I put off reading those two for a long time but they live long in the memory. I appreciate it's perfectly possible you understand the contents of both and you're just trying to keep stuff on-topic. Anyway, my tiny point was made a long time ago so over and out! – Ruben Bartelink Jun 08 '11 at 06:21
  • @RubenBartelink Is the second book you mention "Agile Principles, Patterns, and Practices in C#"? (by Uncle Bob and Micah Martin) – Sudhanshu Mishra Dec 18 '15 at 03:20
  • @dotnetguy Yes, that is the book; sorry for butchering it so badly (I probably had the name of the earlier Java equivalent: "Agile Software Development, Principles, Patterns, and Practices" in mind) – Ruben Bartelink Dec 18 '15 at 09:08
7

Why not just make an interface that enforces immutability (i.e. only getters)?

It's essentially your first solution, but you force the function to use the interface to access the parameter.

public interface IDoSomeActionParameters
{
    string A { get; }
    string B { get; }
    DateTime C { get; }
    OtherEnum D { get; }
    string E { get; }
    string F { get; }              
}

public class DoSomeActionParameters: IDoSomeActionParameters
{
    public string A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public OtherEnum D { get; set; }
    public string E { get; set; }
    public string F { get; set; }        
}

and the function declaration becomes:

public ResultEnum DoSomeAction(IDoSomeActionParameters parameters, out Guid code)

Pros:

  • Doesn't have stack space problem like struct solution
  • Natural solution using language semantics
  • Immutability is obvious
  • Flexible (consumer can use a different class if he wants)

Cons:

  • Some repetitive work (same declarations in two different entities)
  • Developer has to guess that DoSomeActionParameters is a class that could be mapped to IDoSomeActionParameters
Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
trutheality
  • 23,114
  • 6
  • 54
  • 68
  • +1 I don't know why I didn't think of that? :) I guess I thought object would still suffer from a constructor with too many parameters but that's not the case. Yes that's a very valid solution too. The only problem I can think of is it's not really straightforward for API user to find the right class name that supports given interface. Solution that requires the least documentation is better. – Sedat Kapanoglu Jun 05 '11 at 10:28
  • I like this one, the duplication and knowledge of the map are handled with resharper, and I can supply default values using the default constructor of the concrete class – Anthony Johnston Sep 16 '12 at 14:43
  • 3
    I don't like that approach. Someone who has a reference to an arbitrary implementation of `IDoSomeActionParameters` and wants to capture the values therein has no way of knowing whether holding the reference will be sufficient, or if it must copy the values to some other object. Readable interfaces are useful in some contexts, but not as a means of making things immutable. – supercat Sep 25 '12 at 22:24
  • "IDoSomeActionParameters parameters" could be cast to DoSomeActionParameters and changed. The developer may not even realise that they are circumventing an attempt to make parameters imutable – Joseph Simpson Apr 06 '16 at 14:55
7

I'm not a C# programmer but I believe C# supports named arguments: (F# does and C# is largely feature compatable for that sort of thing) It does: http://msdn.microsoft.com/en-us/library/dd264739.aspx#Y342

So calling your original code becomes:

public ResultEnum DoSomeAction( 
 e:"bar", 
 a: "foo", 
 c: today(), 
 b:"sad", 
 d: Red,
 f:"penguins")

this takes no more space/thought that your object creation and has all the benifits, of the fact that you haven't changed what is happening in the unerlying system at all. You don't even have to recode anything to indicate the arguments are named

Edit: here is a artical i found about it. http://www.globalnerdy.com/2009/03/12/default-and-named-parameters-in-c-40-sith-lord-in-training/ I should mention C# 4.0 supports named arguments, 3.0 did not

Frames Catherine White
  • 27,368
  • 21
  • 87
  • 137
  • An improvement indeed. But this only solves code readability and it only does that when the developer opts-in for using named parameters. It's easy for one to forget to specify names and give away the benefits. It does not help when writing the code itself. e.g. when refactoring the function into smaller ones and pass the data around in a single contained packet. – Sedat Kapanoglu Jun 05 '11 at 10:18
6

How about creating a builder class inside your data class. The data class will have all the setters as private and only the builder will be able to set them.

public class DoSomeActionParameters
    {
        public string A { get; private set; }
        public string B  { get; private set; }
        public DateTime C { get; private set; }
        public OtherEnum D  { get; private set; }
        public string E  { get; private set; }
        public string F  { get; private set; }

        public class Builder
        {
            DoSomeActionParameters obj = new DoSomeActionParameters();

            public string A
            {
                set { obj.A = value; }
            }
            public string B
            {
                set { obj.B = value; }
            }
            public DateTime C
            {
                set { obj.C = value; }
            }
            public OtherEnum D
            {
                set { obj.D = value; }
            }
            public string E
            {
                set { obj.E = value; }
            }
            public string F
            {
                set { obj.F = value; }
            }

            public DoSomeActionParameters Build()
            {
                return obj;
            }
        }
    }

    public class Example
    {

        private void DoSth()
        {
            var data = new DoSomeActionParameters.Builder()
            {
                A = "",
                B = "",
                C = DateTime.Now,
                D = testc,
                E = "",
                F = ""
            }.Build();
        }
    }
marto
  • 4,170
  • 1
  • 28
  • 39
  • 1
    +1 That is a perfectly valid solution but I think it's too much scaffolding to maintain such a simple design decision. Especially when the "readonly struct" solution is "very" close to ideal. – Sedat Kapanoglu Jun 04 '11 at 22:42
  • 2
    How does this solve the "too many parameters" problem? The syntax might be different, but the problem looks the same. This is not a criticism, I'm just curious because I am not familiar with this pattern. – alexD Jun 05 '11 at 02:17
  • 1
    @alexD this solves the problem with having too many function parameters and keeping the object immutable. Only the builder class can set the private properties and once you get the parameters object you can't change it. The problem is it requires a lot of scaffolding code. – marto Jun 05 '11 at 07:19
  • 5
    Parameter object in your solution is not immutable. Someone who saves the builder can edit parameters even after building – astef Sep 12 '14 at 15:18
  • 2
    To @astef's point, as it's currently written a `DoSomeActionParameters.Builder` instance can be used to create and configure exactly one `DoSomeActionParameters` instance. After calling `Build()` subsequent changes to the `Builder`'s properties will continue modifying the _original_ `DoSomeActionParameters` instance's properties, and subsequent calls to `Build()` will continue to return the same `DoSomeActionParameters` instance. It should really be `public DoSomeActionParameters Build() { var oldObj = obj; obj = new DoSomeActionParameters(); return oldObj; }`. – Lance U. Matthews Jul 17 '17 at 04:49
3

I know this is an old question but I thought I'd wade in with my suggestion as I've just had to solve the same problem. Now, admittadly my problem was slightly different to yours as I had the additional requirement of not wanting users to be able to construct this object themselves (all hydration of the data came from the database, so I could jail off all construction internally). This allowed me to use a private constructor and the following pattern;

    public class ExampleClass
    {
        //create properties like this...
        private readonly int _exampleProperty;
        public int ExampleProperty { get { return _exampleProperty; } }

        //Private constructor, prohibiting construction outside of this class
        private ExampleClass(ExampleClassParams parameters)
        {                
            _exampleProperty = parameters.ExampleProperty;
            //and so on... 
        }

        //The object returned from here will be immutable
        public ExampleClass GetFromDatabase(DBConnection conn, int id)
        {
            //do database stuff here (ommitted from example)
            ExampleClassParams parameters = new ExampleClassParams()
            {
                ExampleProperty = 1,
                ExampleProperty2 = 2
            };

            //Danger here as parameters object is mutable

            return new ExampleClass(parameters);    

            //Danger is now over ;)
        }

        //Private struct representing the parameters, nested within class that uses it.
        //This is mutable, but the fact that it is private means that all potential 
        //"damage" is limited to this class only.
        private struct ExampleClassParams
        {
            public int ExampleProperty { get; set; }
            public int AnotherExampleProperty { get; set; }
            public int ExampleProperty2 { get; set; }
            public int AnotherExampleProperty2 { get; set; }
            public int ExampleProperty3 { get; set; }
            public int AnotherExampleProperty3 { get; set; }
            public int ExampleProperty4 { get; set; }
            public int AnotherExampleProperty4 { get; set; } 
        }
    }
Mikey Hogarth
  • 4,672
  • 7
  • 28
  • 44
2

here is a slightly different one from Mikeys but what I am trying to do is make the whole thing as little to write as possible

public class DoSomeActionParameters
{
    readonly string _a;
    readonly int _b;

    public string A { get { return _a; } }

    public int B{ get { return _b; } }

    DoSomeActionParameters(Initializer data)
    {
        _a = data.A;
        _b = data.B;
    }

    public class Initializer
    {
        public Initializer()
        {
            A = "(unknown)";
            B = 88;
        }

        public string A { get; set; }
        public int B { get; set; }

        public DoSomeActionParameters Create()
        {
            return new DoSomeActionParameters(this);
        }
    }
}

The DoSomeActionParameters is immutable as it can be and cannot be created directly as its default constructor is private

The initializer is not immutable, but only a transport

The usage takes advantage of the initializer on the Initializer (if you get my drift) And I can have defaults in the Initializer default constructor

DoSomeAction(new DoSomeActionParameters.Initializer
            {
                A = "Hello",
                B = 42
            }
            .Create());

The parameters will be optional here, if you want some to be required you could put them in the Initializer default constructor

And validation could go in the Create method

public class Initializer
{
    public Initializer(int b)
    {
        A = "(unknown)";
        B = b;
    }

    public string A { get; set; }
    public int B { get; private set; }

    public DoSomeActionParameters Create()
    {
        if (B < 50) throw new ArgumentOutOfRangeException("B");

        return new DoSomeActionParameters(this);
    }
}

So now it looks like

DoSomeAction(new DoSomeActionParameters.Initializer
            (b: 42)
            {
                A = "Hello"
            }
            .Create());

Still a little kooki I know, but going to try it anyway

Edit: moving the create method to a static in the parameters object and adding a delegate which passes the initializer takes some of the kookieness out of the call

public class DoSomeActionParameters
{
    readonly string _a;
    readonly int _b;

    public string A { get { return _a; } }
    public int B{ get { return _b; } }

    DoSomeActionParameters(Initializer data)
    {
        _a = data.A;
        _b = data.B;
    }

    public class Initializer
    {
        public Initializer()
        {
            A = "(unknown)";
            B = 88;
        }

        public string A { get; set; }
        public int B { get; set; }
    }

    public static DoSomeActionParameters Create(Action<Initializer> assign)
    {
        var i = new Initializer();
        assign(i)

        return new DoSomeActionParameters(i);
    }
}

So the call now looks like this

DoSomeAction(
        DoSomeActionParameters.Create(
            i => {
                i.A = "Hello";
            })
        );
Anthony Johnston
  • 9,405
  • 4
  • 46
  • 57
2

You could use a Builder-style approach, though depending on the complexity of your DoSomeAction method, this might be a touch heavyweight. Something along these lines:

public class DoSomeActionParametersBuilder
{
    public string A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public OtherEnum D { get; set; }
    public string E { get; set; }
    public string F { get; set; }

    public DoSomeActionParameters Build()
    {
        return new DoSomeActionParameters(A, B, C, D, E, F);
    }
}

public class DoSomeActionParameters
{
    public string A { get; private set; }
    public string B { get; private set; }
    public DateTime C { get; private set; }
    public OtherEnum D { get; private set; }
    public string E { get; private set; }
    public string F { get; private set; }

    public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d, string e, string f)
    {
        A = a;
        // etc.
    }
}

// usage
var actionParams = new DoSomeActionParametersBuilder
{
    A = "value for A",
    C = DateTime.Now,
    F = "I don't care for B, D and E"
}.Build();

result = foo.DoSomeAction(actionParams, out code);
2

In addition to manji response - you may also want to split one operation into several smaller ones. Compare:

 BOOL WINAPI CreateProcess(
   __in_opt     LPCTSTR lpApplicationName,
   __inout_opt  LPTSTR lpCommandLine,
   __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
   __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
   __in         BOOL bInheritHandles,
   __in         DWORD dwCreationFlags,
   __in_opt     LPVOID lpEnvironment,
   __in_opt     LPCTSTR lpCurrentDirectory,
   __in         LPSTARTUPINFO lpStartupInfo,
   __out        LPPROCESS_INFORMATION lpProcessInformation
 );

and

 pid_t fork()
 int execvpe(const char *file, char *const argv[], char *const envp[])
 ...

For those who don't know POSIX the creation of child can be as easy as:

pid_t child = fork();
if (child == 0) {
    execl("/bin/echo", "Hello world from child", NULL);
} else if (child != 0) {
    handle_error();
}

Each design choice represent trade-off over what operations it may do.

PS. Yes - it is similar to builder - only in reverse (i.e. on callee side instead of caller). It may or may not be better then builder in this specific case.

Maciej Piechotka
  • 7,028
  • 6
  • 39
  • 61
  • It is a small operation but that's a good recommendation for anyone who is violating single-responsibility principle. My question takes place just after all other design improvements have been done. – Sedat Kapanoglu Jun 05 '11 at 10:20
  • Ups. Sorry - I ready a question before your edit and posted it later - hence I hadn't notice it. – Maciej Piechotka Jun 05 '11 at 11:37
1

Use the structure, but instead of public fields, have public properties:

•Everybody (including FXCop & Jon Skeet) agree that exposing public fields are bad.

Jon and FXCop will be satisified because you are exposing properites not fields.

•Eric Lippert et al say relying on readonly fields for immutability is a lie.

Eric will be satisifed because using properties, you can ensure that the value is only set once.

    private bool propC_set=false;
    private date pC;
    public date C {
        get{
            return pC;
        }
        set{
            if (!propC_set) {
               pC = value;
            }
            propC_set = true;
        }
    }

One semi-immutable object (value can be set but not changed). Works for value and Reference types.

jmoreno
  • 12,752
  • 4
  • 60
  • 91
  • +1 Maybe private readonly fields and public getter-only properties could be combined in a struct to allow a less verbose solution. – Sedat Kapanoglu Jun 05 '11 at 10:25
  • Public read-write properties on structures that mutate `this` are far more evil than public fields. I'm not sure why you think only setting the value once makes things "okay"; if one starts with a default instance of a struct, the problems associated with `this`-mutating properties will still exist. – supercat Sep 25 '12 at 22:27
0

A variant of Samuel's answer that I used in my project when I had the same problem:

class MagicPerformer
{
    public int Param1 { get; set; }
    public string Param2 { get; set; }
    public DateTime Param3 { get; set; }

    public MagicPerformer SetParam1(int value) { this.Param1 = value; return this; }
    public MagicPerformer SetParam2(string value) { this.Param2 = value; return this; }
    public MagicPerformer SetParam4(DateTime value) { this.Param3 = value; return this; }

    public void DoMagic() // Uses all the parameters and does the magic
    {
    }
}

And to use:

new MagicPerformer().SeParam1(10).SetParam2("Yo!").DoMagic();

In my case the parameters were intentionally modifiable, because the setter methods didn't allow for all possible combinations, and just exposed common combinations of them. That's because some of my parameters were pretty complex and writing methods for all possible cases would have been difficult and unnecessary (crazy combinations are rarely used).

Community
  • 1
  • 1
Vilx-
  • 104,512
  • 87
  • 279
  • 422