0

REMARK after rethinking my issue. Though the best solution for my exact example with WriteData method was proposed by @Dogu Arslan, the issue in question's title was actually solved by @InBetween and @Fabio. I. e. for my exact example with WriteData method it is better to move the conversion logic out of WriteData method, but to abstract the logic from unrelated types it is better to use proposed overloading. So all the three answers are helpful for me.

I already have read the following similar questions - 1, 2, and others - but not have come with something suitable solution in my case.

My case is: I have the following simple method. The important part is the line with commentary and the "source" parameter. The rest part is not so important and serves only to show that there is an "operation" from the question's title.

void WriteData(
    int count, 
    int currentIndex, 
    List<byte> source, 
    StreamWriter sw, 
    string fileName)
{
    var countToWrite = count - currentIndex;

    if (countToWrite == 0)
        return;

    if (sw == null)
        sw = new StreamWriter(GetFullPath(fileName));

    //---------- Source's elements must be converted to string.
    var dataToWrite =
        source.GetRange(currentIndex, countToWrite)
        .Select(x => Convert.ToString(x));

    StringBuilder sb = new StringBuilder();

    foreach (var item in dataToWrite)
        sb.AppendLine(item);

    sw.Write(sb.ToString());
}

For now I want the "source" parameter to be list with bytes, or doubles, or strings. Have I write three copies of WriteData method with only single change - the type of list in "source"? Or there is a better approach?

I tried type constraint, but to what type to constrain?

I tried to check the type and to throw exceptions if it is not in my type list (byte, double, string). But exceptions work only at run-time, and I want it to work in compile-time.

For now I have to restrict myself with run-time type checking as temporary solution, but, as I mentioned before, it is not suitable in perspective.

Community
  • 1
  • 1
Alex34758
  • 396
  • 4
  • 14

3 Answers3

3

Why not using overload methods with one "hided" actual solution

public void WriteData(List<byte> source) { WriteData<byte>(source); }
public void WriteData(List<double> source) { WriteData<double>(source); }
public void WriteData(List<string> source) { WriteData<string>(source); }

private void WriteData<T>(List<T> source)
{
     // Your actual implementation
}

In private method you can check types and throw exception when wrong type is given, in case you want "protect" from making private method public by "mistake".

Fabio
  • 31,528
  • 4
  • 33
  • 72
  • Throwing exceptions is related to run-time, but I want a compile-time error or warning. – Alex34758 Apr 18 '17 at 09:55
  • 1
    @Alex34758, throwing exception was just one more layer of "protection". public overloads with explicitly declared types provide you with compile-time checking – Fabio Apr 18 '17 at 09:58
  • Your solution is good for my exact question, but I agreed with @DoguArslan that the issue have to be rethinked, and it is better to remove the conversion logic out of WriteData method (I will convert the "source" with Convert class before calling the WriteData method, then pass the result as the "source" parameter which type will be List) so the issue will be eliminated. The right architecture is the key. – Alex34758 Apr 19 '17 at 12:05
  • 1
    Moving responsibility of converting outside of the method is correct refactoring of your design. Anyway all three answers provide you same solution - overload methods. Constructors is same method as others. – Fabio Apr 19 '17 at 12:11
2

The best solution is to publicly expose the needed overloads and then delegate implementation to a private generic method:

public void WriteData( , , List<string> source, , ) { WriteData<string>(...); }
public void WriteData( , , List<byte> source, , ) { WriteData<byte>(...); }
public void WriteData( , , List<double> source, , ) { WriteData<double>(...); }

private void WriteData<T>( , , List<T> source, , ) 
{ 
    Debug.Assert(typeof(T).Equals(typeof(string)) ||
                 typeof(T).Equals(typeof(byte)) ||
                 typeof(T).Equals(typeof(double)));
    ...
}
InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Oh, you do it like @Fabio does. But why to have Debug.Assert if explicit type for List elements in public WriteData method already restricted the generic type T? It seems Debug.Assert will never have the "false" condition. – Alex34758 Apr 18 '17 at 10:19
  • 2
    @Alex34758 Hence the `Debug.Assert` and not a `throw` whatever exception. Nobody *outside* your class will call with an invalid type (barring reflection), but nothing is stopping an invalid call from *inside* your class. The assert is there as a safety net. – InBetween Apr 18 '17 at 10:23
  • Your solution is good for my exact question, but I agreed with @DoguArslan that the issue have to be rethinked, and it is better to remove the conversion logic out of WriteData method (I will convert the "source" with Convert class before calling the WriteData method, then pass the result as the "source" parameter which type will be List) so the issue will be eliminated. The right architecture is the key. – Alex34758 Apr 19 '17 at 12:05
1

One thing you could do is to remove the string conversion responsibility to another class altogether.ie. StringConverter class which would have overloaded constructors that would take these different input types to convert them to string and provide a method that returns a string. That way even if you extend in future the types you support you would not need to change this method here in your example, all of that would be transparent behind you string converter class. The checks will also be compile time because string converter class would take the input through its constructor. Yes this would mean everytime you want to call WriteData method you would need to instantiate a new StringConverter object but C# does not provide type constraints on the level you ask out of the shelf.

Dogu Arslan
  • 3,292
  • 24
  • 43
  • I agree with you. Even though InBetween and Fabio propose good solutions also, I think the best way is to move the conversion logic out of WriteData method. – Alex34758 Apr 19 '17 at 11:56