6

I have a function as below:

  public var UpdateMapFetcher(int stationID, int typeID)

I need this function to return either string or int.

My return value is set as below

 if (finaloutput == "System.String")
        {
            // param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
            returnvalue = returnvalue.ToString();
            return returnvalue;
        }
        else if (finaloutput == "System.Int32")
        {
            int a=0;
            a = Convert.ToInt32(returnvalue);
            return a;
        }

How to have either one data type as return value in dynamic environment.

Kanes
  • 105
  • 1
  • 10
  • Can you better describe why that is what you need? (You can always return an `object` but I think maybe you should rethink a bit design) – Gilad Green Aug 22 '16 at 05:32
  • `var` is just "compiler, please figure out the correct data type and put it here". It still has to be determined *at compile time*. – Damien_The_Unbeliever Aug 22 '16 at 06:43

8 Answers8

6

My intuition tells me, that you are trying to convert string value to some type. In that case you can use:

public T UpdateMapFetcher<T>(int stationID)
{
    //var someValue = "23";
    return (T)Convert.ChangeType(someValue, typeof(T));
}
//then
var typed = UpdateMapFetcher<int>(6);

In case you don't know T, you can use mapping (0-int, 1-string, etc.):

public object UpdateMapFetcher(int stationID, int type)
{
    var typeMap = new []{ typeof(int), typeof(string)};
    //var someValue = "23";
    return Convert.ChangeType(someValue, typeMap[type]);
}
//then
var untyped = UpdateMapFetcher(6, 0/*0 is int*/);
if (untyped.GetType() == typeof(int))
{ /*is int*/
}

Another solution is to use implicit conversions:

public class StringOrInt
{
    private object value;
    public ValueType Type { get; set; }

    public static implicit operator StringOrInt(string value)
    {
        return new StringOrInt()
        {
            value = value,
            Type = ValueType.String
        };
    }
    public static implicit operator StringOrInt(int value)
    {
        return new StringOrInt()
        {
            value = value,
            Type = ValueType.Int
        };
    }
    public static implicit operator int(StringOrInt obj)
    {
        return (int)obj.value;
    }
    public static implicit operator string(StringOrInt obj)
    {
        return (string)obj.value;
    } 
}
public enum ValueType
{
    String,
    Int
}

And then (simplified):

public static StringOrInt UpdateMapFetcher(int stationID, int typeID)
{
    if (typeID == 0)
        return "Text";
    return 23;
}

private static void Main(string[] args)
{
    var result = UpdateMapFetcher(1, 1);
    if (result.Type == ValueType.String) { }//can check before
    int integer = result;//compiles, valid
    string text = result;//compiles, fail at runtime, invalid cast      
}
Paweł Dyl
  • 8,888
  • 1
  • 11
  • 27
1

you can return an object. You'd have to subsequently check for types in your consuming method. I assume that won't be a problem in your usecase.

your method signature is therefore:

public object UpdateMapFetcher(int stationID, int typeID)
Louis
  • 1,194
  • 1
  • 10
  • 15
1

You also have the option of using the out keyword, which permits you to accept both into variables and check after the function has been called.

public void UpdateMapFetcher(int stationID, int typeID, out int intValue, out string strValue)

// or int return val and out string value

public int UpdateMapFetcher(int stationID, int typeID, out string strValue)

With the use appearing something like this:

int intVal;
string strVal;

UpdateMapFetcher(stationID, typeID, out intVal, out strVal);

if (strVal != null) 
{ 
    doSomethingWithString(strVal); 
}
else
{
    doSomethingWithInt(intVal);
}
khargoosh
  • 1,450
  • 15
  • 40
1

Frankly, I would just return a Tuple, with string being non-null indicating string value to use, and null as indicator for int return

public Tuple<string, int> UpdateMapFetcher(int stationID, int typeID) {
    if (finaloutput == "System.String")
    {
        // param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
        returnvalue = returnvalue.ToString();
        return new Tuple<string, int>(returnvalue, 0);
    }
    else if (finaloutput == "System.Int32")
    {
        int a=0;
        a = Convert.ToInt32(returnvalue);
        return new Tuple<string, int>(null, a);
    }

}

On consumer side

var rc = UpdateMapFetcher( .... );

if (rc.Item1 != null) {
    // code to use string value
} else {
   // code to use int value
}
Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
1

I would choose to return an object of new class which might look like this:

class Result {

    public string StringValue { get; }

    public string Int32Value { get; }

    public bool IsString { get; }

    public bool IsInt32 { get; }

    public Result(string value) {
        StringValue = value;
        IsString = true;
    }

    public Result(int value) {
        Int32Value = value;
        IsInt32 = true;
    }
}

This way you can check which Type is it by using Isxxx property. You can also enhance this with validation in value geters. F. e., for string it might look like this:

public string StringValue {
    get {
        if (IsString)
            return m_stringValue;
        throw new InvalidOperationException("Value is not a string.");
    }
}
Dovydas Šopa
  • 2,282
  • 8
  • 26
  • 34
1

You can't really do exactly that, but there are several ways to do more or less what you want. You'd probably be better off change the design a little though.

Two ideas:

  • Either change your code to use two different methods, and call each of them as needed instead.
  • ..Or return an object, which you can cast however you like..
  • ..Or, use a generic method with TypeDescriptor, like the following.

Note that we here convert the value to string first even if it was an int, since we can then use a common method ConvertFromString() to convert it to whatever type T was.

public T UpdateMapFetcher<T>(int stationID, int typeID) {
    // To allow parsing to the generic type T:
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
    if(converter != null)
    {
        return (T)converter.ConvertFromString(returnvalue.ToString());
    }    
    else
    {
        return default(T);
    }
}

Usage:

var result = MyExtensions.UpdateMapFetcher<string>(1, 2);

or:

var result = MyExtensions.UpdateMapFetcher<int>(1, 2);
Kjartan
  • 18,591
  • 15
  • 71
  • 96
0

You can return Object and cast to type which you want.

public Object UpdateMapFetcher(int stationID, int typeID)

if (finaloutput == "System.String")
        {
            // param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
            returnvalue = returnvalue.ToString();
            return returnvalue;
        }
        else if (finaloutput == "System.Int32")
        {
            int a=0;
            a = Convert.ToInt32(returnvalue);
            return a;
        }
PhuocTran
  • 1
  • 2
0

A type that can contain either one type or another is usually called (unsurprisingly) Either. It is a special case of a sum type, basically a discriminated union, tagged union, or disjoint union with exactly two cases (instead of an arbitrary number).

Unfortunately, there does not exist an implementation of an Either type in the standard libraries, but there are plenty of implementations to be found on Google, GitHub, and elsewhere … and porting one of the existing implementations from e.g. Haskell or Scala isn't that hard, either.

It looks a bit like this (forgive my code, I don't actually know C♯ that well):

using System;

abstract class Either<A, B>
{
    public abstract bool IsLeft { get; }
    public abstract bool IsRight { get; }
    public abstract A Left { get; }
    public abstract B Right { get; }
    public abstract A LeftOrDefault { get; }
    public abstract B RightOrDefault { get; }
    public abstract void ForEach(Action<A> action);
    public abstract void ForEach(Action<B> action);
    public abstract void ForEach(Action<A> leftAction, Action<B> rightAction);

    private sealed class L : Either<A, B>
    {
        private A Value { get; }
        public override bool IsLeft => true;
        public override bool IsRight => false;
        public override A Left => Value;
        public override B Right { get { throw new InvalidOperationException(); } }
        public override A LeftOrDefault => Value;
        public override B RightOrDefault => default(B);
        public override void ForEach(Action<A> action) => action(Value);
        public override void ForEach(Action<B> action) {}
        public override void ForEach(Action<A> leftAction, Action<B> rightAction) => leftAction(Value);

        internal L(A value) { Value = value; }
    }

    private sealed class R : Either<A, B>
    {
        private B Value { get; }
        public override bool IsLeft => false;
        public override bool IsRight => true;
        public override A Left { get { throw new InvalidOperationException(); } }
        public override B Right => Value;
        public override A LeftOrDefault => default(A);
        public override B RightOrDefault => Value;
        public override void ForEach(Action<A> action) {}
        public override void ForEach(Action<B> action) => action(Value);
        public override void ForEach(Action<A> leftAction, Action<B> rightAction) => rightAction(Value);

        internal R(B value) { Value = value; }
    }

    public static Either<A, B> MakeLeft(A value) => new L(value);
    public static Either<A, B> MakeRight(B value) => new R(value);
}

And you'd use it like this:

static class Program
{
    public static void Main()
    {
        var input = Console.ReadLine();
        int intResult;
        var result = int.TryParse(input, out intResult) ? Either<int, string>.MakeLeft(intResult) : Either<int, string>.MakeRight(input);
        result.ForEach(r => Console.WriteLine("You passed me the integer one less than " + ++r), r => Console.WriteLine(r));
    }
}
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653