2016-08-22 3 views
6

У меня есть функция, как показано ниже:установить несколько возвращаемого значения для метода декларации

public var UpdateMapFetcher(int stationID, int typeID) 

Мне нужна эта функция, чтобы возвращать либо строку или Int.

Моя возвращаемое значение устанавливается следующим образом

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; 
     } 

Как иметь либо один тип данных в качестве возвращаемого значения в динамической среде.

+0

Может вам лучше описать, почему именно это вам нужно? (Вы всегда можете вернуть «объект», но я думаю, может быть, вам стоит переосмыслить немного дизайна). –

+0

«var» - это просто «компилятор», пожалуйста, выясните правильный тип данных и поместите его здесь ». Он все еще должен быть определен * во время компиляции *. –

ответ

6

Моя интуиция говорит мне, что вы пытаетесь преобразовать значение строки в какой-то тип. В этом случае вы можете использовать:

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

В случае, если вы не знаете, T, вы можете использовать отображение (0-ИНТ, 1-строки и т.д.):

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*/ 
} 

Другим решением является использование неявного преобразования:

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 
} 

А потом (simplifie d):

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  
} 
1

вы можете вернуть объект. Вам придется впоследствии проверять типы в методе потребления. Я предполагаю, что это не будет проблемой в вашей работе.

ваш метод подписи поэтому:

public object UpdateMapFetcher(int stationID, int typeID) 
0

Вы можете вернуть объект и преобразованный к типу, который вы хотите.

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; 
     } 
1

У вас также есть возможность использования out keyword, что позволяет Вам принимать как в переменные и проверить после того, как функция была вызвана.

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) 

С помощью появляющегося что-то вроде этого:

int intVal; 
string strVal; 

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

if (strVal != null) 
{ 
    doSomethingWithString(strVal); 
} 
else 
{ 
    doSomethingWithInt(intVal); 
} 
1

Честно говоря, я бы просто вернуть кортеж, с струна быть ненулевой с указанием строки значения для использования, и нуль в качестве индикатора для ИНТА возврата

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); 
    } 

} 

На стороне потребителя

var rc = UpdateMapFetcher(....); 

if (rc.Item1 != null) { 
    // code to use string value 
} else { 
    // code to use int value 
} 
1

Я выбрал бы возвращать object нового class, который может выглядеть следующим образом:

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; 
    } 
} 

Таким образом, вы можете проверить, какие Type является его помощью Isxxx собственности. Вы также можете улучшить это с помощью валидации в значении get. F. e.Для string это может выглядеть следующим образом:

public string StringValue { 
    get { 
     if (IsString) 
      return m_stringValue; 
     throw new InvalidOperationException("Value is not a string."); 
    } 
} 
1

Вы не можете действительно сделать точно, но есть несколько способов сделать более или менее то, что вы хотите. Вы, вероятно, лучше измените дизайн, хотя.

Две идеи:

  • Либо изменить код, чтобы использовать два различных метода, и назвать каждую из них по мере необходимости вместо этого.
  • ..Or возвратите объект, который вы можете использовать, как вам нравится.
  • .. Или используйте общий метод с TypeDescriptor, как показано ниже.

Обратите внимание, что мы здесь преобразовать значение в строку первой, даже если это была ИНТ, так как мы можем использовать общий метод ConvertFromString(), чтобы преобразовать его в любой тип T был.

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); 
    } 
} 

Использование:

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

или:

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

тип, который может содержать либо один тип или другой, обычно называют (Unsurprisingly) Either. Это особый случай sum type, в основном дискриминированный союз, tagged union, или несвязанный союз с ровно двумя случаями (вместо произвольного числа).

К сожалению, в стандартных библиотеках не существует реализация типа Either, но есть множество реализаций, которые можно найти в Google, GitHub и других местах ... и портирование одной из существующих реализаций, например. Хаскелл или Скала не так уж и трудны.

Это выглядит как это (простите мой код, я не знаю, на самом деле C♯, что хорошо):

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); 
} 

И вы бы использовать его как это:

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)); 
    } 
}