7

For example, you want an object to be possibly initialised in two ways, using a file path and using a string. Normally both constructors should take one string parameter, MyObject(string file) and MyObject(string content), but it is impossible to overload this way. What do you suggest?

Edit: In the first case, the file path is also needed, so please don't suggest a solution that reads the file content and just pass the content to the other constructor.

Louis Rhys
  • 34,517
  • 56
  • 153
  • 221
  • 3
    Wrap one of the string types in its own class, and accept that type as a parameter instead. – Cody Gray - on strike Feb 15 '11 at 05:36
  • @Cody Gray: Hi, I think that merits to be an answer. – Louis Rhys Feb 15 '11 at 05:59
  • I didn't post it as an answer because I think the one provided by Mark Byers is the best solution in this case. (I upvoted that one instead.) No reason to create an additional wrapper class when there's a type built in that will do the job instead. For more general applications, my suggestion is probably the way to go, however. – Cody Gray - on strike Feb 15 '11 at 06:16
  • I would go for either the static factory method or create a constructor that accepts both arguments and throws an exception if both args are != null. I'm saying this because not always (my case - I have a similar problem), the types are really strings and I don't find it particularly elegant to throw in a new class that represents a string – Żubrówka Oct 29 '15 at 10:07

7 Answers7

17

I'm not a C# programmer but this looks like a job for the static factory method pattern:

class MyObject {
  public static MyObject FromContent(string content) {
    return new MyObject(content);
  }

  public static MyObject FromFile(string path) {
    return new MyObject(ReadContentFromFile(path));
  }
}

Then you can do

MyObject object = MyObject.FromFile("/some/path");

which is even more readable than using a regular constructor.

sjr
  • 9,769
  • 1
  • 25
  • 36
  • in the second case, I still need the path, which I will lose if I use this pattern. – Louis Rhys Feb 15 '11 at 05:39
  • You have this problem anyway right? Why not have MyObject.GetPath() that returns null if MyObject was not created from a path? – sjr Feb 15 '11 at 05:40
  • 2
    @Louis: create a private setter for the path that MyObject.FromFile() can use to store that information, or have a private constructor overload that takes an additional dummy parameter to indicate the string is a path - these might be warts in the design, but since they're private, and the resulting public interface is nice and clean it might be a reasonable trade-off. – Michael Burr Feb 15 '11 at 05:46
11

Perhaps you could change the first to accept a FileInfo instead:

class MyObject
{
    public MyObject(FileInfo file) { /* etc... */ }
    public MyObject(string content) { /* etc... */ }
}

...

MyObject o = new MyObject(new FileInfo(filename));
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • Or more generally, wrap one of the string types in its own class, and accept that type as a parameter instead (in this case the wrapper is FileInfo) – Louis Rhys Feb 16 '11 at 03:14
1

Or create factory methods:

public static MyObject CreateByFilePath(string path){ ... }
public static MyObject CreateByContent(string content){ ... }
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
1

Maybe you can use a factory?

class MyObjectProvider
{
   public static MyObject CreateByPath(string path) 
   { 
      return new MyObject
              {
                  Path = path;
              };

   }

   public static MyObject CreateByContent(string content) 
   { 
      return new MyObject
              {
                  Content = content;
              };
   }
}
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
1

I think you refer this question. ::

Calling constructor overload when both overload have same signature

Community
  • 1
  • 1
sikender
  • 5,883
  • 7
  • 42
  • 80
0
public MyObject(Uri fileUri);
public MyObject(string content);
Alex Zhevzhik
  • 3,347
  • 19
  • 19
0

I'll take the contrarian approach and say don't do it. The best of the solutions posted so far (use an alternate type such as FileInfo or Uri for one of the overloads) seems a bit hackish to me - goes against the principle of least surprise.

If you can construct using the content only without a filename, it follows that the filename is not essential. And similarly if you can construct with a filename only it follows that the content is not essential. Presumably you can subsequently set the missing filename/content later, e.g. by setting a property:

MyObject myObject = new MyObject(fileName);
myObject.Content = ...

MyObject myObject = new MyObject(content);
myObject.FileName = ...

Instead of trying to fight it, choose one of your parameters as being the most important (fileName in the following example), and create two constructurs as follows:

public MyObject(string fileName) : this(fileName, null)
{
}

public MyObject(string fileName, string content)
{
    ... implementation
}

You can allow null to be passed for either or both parameters if it makes sense. And you can insist that at least one is not null if that's appropriate:

public MyObject(string fileName, string content)
{
    if (fileName == null && content == null) throw new ArgumentException(...);
    ...
}    

In short, don't use hacks to work around restrictions like this.

Joe
  • 122,218
  • 32
  • 205
  • 338