2016-08-22 18 views
6

Mam funkcji, jak poniżej:ustawić wartość wielokrotny powrót do deklaracji metody

public var UpdateMapFetcher(int stationID, int typeID) 

muszę tę funkcję, aby powrócić albo ciąg lub int.

Moja zwracana wartość jest ustawiona poniżej

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

Jak mieć jeden typ danych jako wartości zwracanej w dynamicznym środowisku.

+0

Can lepiej opisać, dlaczego tego właśnie potrzebujesz? (Zawsze możesz zwrócić 'obiekt', ale myślę, że powinieneś przemyśleć nieco projekt) –

+0

' var' jest po prostu "kompilatorem, proszę sprawdź poprawny typ danych i umieść go tutaj". Musi jeszcze zostać ustalony * podczas kompilacji *. –

Odpowiedz

6

Moja intuicja podpowiada mi, że próbujesz przekonwertować wartość ciągu na jakiś typ. W takim przypadku można użyć:

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

w przypadku, gdy nie wiem, T, można użyć mapowania (0-int, 1-ciąg, 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*/ 
} 

Innym rozwiązaniem jest użycie niejawne konwersje:

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 
} 

A potem (simplifié 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

można zwrócić obiekt. Następnie musisz sprawdzić typy w swojej metodzie konsumowania. Zakładam, że nie będzie to problemem w twoim przypadku.

podpis metoda jest zatem:

public object UpdateMapFetcher(int stationID, int typeID) 
0

Można zwrócić przedmiot i rzutować na typ, który chcesz.

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

Masz również możliwość korzystania z out keyword, który pozwala na akceptować zarówno do zmiennych i sprawdzić po funkcja została wywołana.

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) 

Z wykorzystaniem pojawiających coś takiego:

int intVal; 
string strVal; 

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

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

Szczerze mówiąc, chciałbym tylko zwrócić krotka, ze ciąg jest niezerowe wskazujący wartość ciągu w użyciu i null jako wskaźnik int zamian

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

} 

Po stronie konsumenta

var rc = UpdateMapFetcher(....); 

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

wybrałbym do zwracania object nowej class który może wyglądać następująco:

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

W ten sposób można sprawdzić, które Type jest go za pomocą Isxxx nieruchomości. Możesz również ulepszyć to poprzez walidację w wartości get. F. e.Dla string może to wyglądać tak:

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

Naprawdę nie można zrobić dokładnie, ale istnieje kilka sposobów, aby zrobić więcej lub mniej, co chcesz. Najprawdopodobniej lepiej byłoby zmienić projekt nieco.

dwie idee:

  • Albo zmienić swój kod do korzystania z dwóch różnych metod, a każdy z nich dzwonić, ile potrzeba zamiast.
  • .. lub zwróć obiekt, który możesz oddać, ale chcesz ..
  • ..Or, użyj ogólnej metody z TypeDescriptor, jak poniżej.

Należy pamiętać, że mamy tu konwersji wartości na ciąg pierwszy, nawet jeśli było to int, ponieważ możemy użyć wspólnej metody ConvertFromString() aby przekształcić go do niezależnie od typu T było.

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

Zastosowanie:

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

czyli

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

typu, który może zawierać jedną lub innego typu jest zwykle nazywany (zadziwiające) Either. Jest to specjalny przypadek sum type, w zasadzie dyskryminowane unia, rekord z wariantami lub suma rozłączna z dokładnie dwóch przypadkach (zamiast dowolnej liczby).

Niestety, nie ma implementacji typu Either w standardowych bibliotekach, ale istnieje wiele implementacji, które można znaleźć w Google, GitHub i gdzie indziej ... i przeniesienie jednej z istniejących implementacji z np. Haskell lub Scala też nie są takie trudne.

To wygląda trochę tak (wybacz mój kod, ja nie wiem C♯ że rzeczywiście dobrze):

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

I chcesz go używać tak:

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