Doogal
Doogal

Reputation: 1667

C# constructors with same parameter signatures

I'm sure this must be a common problem. I've got a class that in an ideal world would have the following constructors

public Thing(string connectionString)

public Thing(string fileName)

Obviously this isn't allowed because the signatures are the same. Does anybody know of an elegant solution to this problem?

Upvotes: 36

Views: 15298

Answers (7)

Sean Bright
Sean Bright

Reputation: 120634

You can used the named constructor idiom:

public class Thing
{
    private string connectionString;

    private string filename;

    private Thing()
    {
        /* Make this private to clear things up */
    }

    public static Thing WithConnection(string connectionString)
    {
        var thing = new Thing();
        thing.connectionString = connectionString;
        return thing;
    }

    public static Thing WithFilename(string filename)
    {
        var thing = new Thing();
        thing.filename = filename;
        return thing;
    }
}

Upvotes: 59

tvanfosson
tvanfosson

Reputation: 532445

These actually seem like different "things" to me, either a class associated with a file or a class associated with a database. I'd define an interface, then have separate implementations for each. Use a Factory to generate the correct implementation.

A hint that you may need to change your design is if your methods have to decide whether they are working with a file or a database before they perform the required action. If this is the case, then separating into different classes would be the way I would go.

public interface IThing
{
   ... methods to do the things that Things do
}

public class FileThing : IThing
{
  ... file-based methods
}

public class DatabaseThing : IThing
{
  ... database-based methods
}

public static class ThingFactory
{
     public IThing GetFileThing( string name )
     {
         return new FileThing( name );
     }

     public IThing GetDatabaseThing( string connectionString )
     {
         return new DatabaseThing( connectionString );
     }
}

If you had common behavior you could alternatively define an abstract class containing the default/common behavior and derive from it instead of/in addition to the interface.

Upvotes: 5

Adam Wright
Adam Wright

Reputation: 49376

Well, there are several potentials - what's considered elegent depends on the usage scenario.

  • Static factory methods, that call into a private constructor.

    static Thing thingWithFileName(string fileName)
    
  • Create a different type for one of the parameters, or use a builtin. Rather than a string fileName, you could use a System.IO.FileStream. This is also more type safe, as I can't accidently pass the wrong data into the wrong static method, or field.

  • Pass a second parameter to the constructor, either an enum or a boolean, indicating the intent of the first parameter

    enum ThingType { FileName, ConnectionString }
    Thing(string str, ThingType type) ...
    
  • Subclass Thing, so you have a ConnectionTypeThing and a FileBackedThing

  • Completely eliminate Thing doing it's connection, and have preconnected data sources provided. So you end up with

    Thing(InputStream dataSource)
    

    or something analogous.

My "elegance" money goes on either the first or second suggestions, but I'd need more context to be happy with any choice.

Upvotes: 9

abelenky
abelenky

Reputation: 64672

I like static constructor-functions:

class Thing
{
   public static Thing NewConnection(string connectionString)
   {
       return new Thing(connectionString, true);
   }

   public static Thing NewFile(string fileName);
   {
        return new Thing(fileName, false);
   }
}
.
.
.
{
    var myObj = Thing.NewConnection("connect=foo");
    var Obj2 = Thing.NewFile("myFile.txt");
}

(not shown, but straight-forward, the implementation of the Thing-Constructor with an extra boolean parameter).

Upvotes: 0

BFree
BFree

Reputation: 103742

Here are some workarounds.

Have one constructor that takes a connection string, and then have a factory method on the class that takes filename. Something like this:

public static Thing CreateThing(string fileName)

this method can call a private parameter less constructor, and you can take it from there.

Another option, is to have an enum that has two types in it. FileName and ConnectionString. Then just have one constructor that takes a string, and the enum. Then based on the enum you can determine which way to go.

Upvotes: 0

Brian
Brian

Reputation: 118855

You can make all the constructors private and create factory methods (static methods on the class like CreateFromConnectionString()).

Upvotes: 5

Gerrie Schenck
Gerrie Schenck

Reputation: 22368

Make two public properties ConnectionString and FileName and then use these to fill your object.

In C# you can use an object initalizer. Like this:

Thing thing = new Thing{FileName = "abc", ConnectionString = "123"};

Upvotes: 1

Related Questions