2016-05-06 2 views
1

Мне интересно, можно ли написать метод C#, который может принимать комбинацию разных типов аргументов.Метод с комбинациями разных типов аргументов

Например, этот метод объединил бы четыре перегруженных метода в один.

+4

В чем польза? Предоставьте несколько перегрузок с различными сигнатурами. –

+2

Внутренне вам нужно будет написать код, чтобы выяснить, какой тип каждого параметра и соответствующим образом обработать, в этот момент вы можете также иметь разный метод перегрузки. –

+0

'JObject' и' File' классы имеют довольно разные члены и не реализуют какой-либо общий интерфейс. Таким образом, этот «комбинированный» метод будет заполнен кодом спагетти, например 'if (jsonOne is File) {.....} else if (isonOne is JObject) {...}' –

ответ

6

Нет, вы не можете создать одну перегрузку метода, которая может принимать один из двух фиксированных типов для заданного параметра.

Лучшее, что вы могли бы сделать (хотя и избегать множественных перегрузок), - это создать новый тип, который вы могли бы создать экземпляр через один из двух разных типов, и принять экземпляр , который.

public class MyJSON 
{ 
    public MyJSON(JObject json) 
    { 
     FirstOption = json; 
     FirstOptionValid = true; 
    } 
    public MyJSON(File json) 
    { 
     SecondOption = json; 
     SecondOptionValid = true; 
    } 
    public JObject FirstOption { get; private set; } 
    public bool FirstOptionValid { get; private set; } 
    public File SecondOption { get; private set; } 
    public bool SecondOptionValid { get; private set; } 

    public static implicit operator MyJSON(File json) 
    { 
     return new MyJSON(json); 
    } 
    public static implicit operator MyJSON(JObject json) 
    { 
     return new MyJSON(json); 
    } 
} 

Можно даже создавать неявные преобразования из JObject и File в MyJSON, так что пользователь может на самом деле пройти JObject или File объект MyMethod, даже без необходимости явно построить промежуточный объект вообще.

На самом деле, это на самом деле очень легко обобщить это для любого фиксированного числа типов с использованием дженериков:

public class Option<T1, T2> 
{ 
    public Option(T1 value) 
    { 
     FirstOption = value; 
     FirstOptionValid = true; 
    } 
    public Option(T2 value) 
    { 
     SecondOption = value; 
     SecondOptionValid = true; 
    } 
    public T1 FirstOption { get; private set; } 
    public bool FirstOptionValid { get; private set; } 
    public T2 SecondOption { get; private set; } 
    public bool SecondOptionValid { get; private set; } 

    public static implicit operator Option<T1, T2>(T2 value) 
    { 
     return new Option<T1, T2>(value); 
    } 
    public static implicit operator Option<T1, T2>(T1 value) 
    { 
     return new Option<T1, T2>(value); 
    } 
} 

Теперь мы можем написать свой метод, как:

public static void MyMethod(Option<JObject, File> json1, Option<JObject, File> json2) 
+0

Это, хотя, как уже было сказано, было бы гораздо разумнее иметь несколько перегрузок с разными сигнатурами для каждого требуемого типа. – GJKH

+0

@GJKH Это зависит от ситуации. Множественные перегрузки были бы хороши для одного параметра с двумя параметрами, но он масштабируется по экспоненте как с количеством опций для любого заданного параметра, так и с количеством параметров, в то время как это масштабируется линейно, поэтому, если существует несколько типов или больше, чем несколько параметров, это, вероятно, будет проще. – Servy

+0

Это может быть расширено таким образом, что 'MyJSON' предоставляет все, что нужно для этого метода, из этих двух типов таким образом, что вам не нужно знать, было ли оно создано из' JObject' или 'File'. Но на самом деле это зависит от того, как иначе их обрабатывают. – juharr

1

Два возможных, но грязные решения:

Один (принимающие объекты и проверки типов)

public static void MyMethod(object one, object two) 
{ 
    if(one is JObject) // handle 
    else if(one is File) // handle 
    else throw SomeException(); 
    ... 

же для второго паров

Два: факультативный PARAMS

public static void MyMethod(JObject jone = null, File fone = null, 
    JObject jtwo = null, File ftwo = null) 
{ 
    if(jone == null && fone == null) 
     throw SomeException(); 
    else 
     // use set values and do stuff 
    ... 
+0

Да, точки должны указывать, что ... вы должны сделать это для каждого параметра, я опустил вторую часть. –

+0

обновил мой ответ для лучшей удобочитаемости –

+0

Обратите внимание, что * оба этих решения теряют безопасность в статическом типе, первая полностью утрачивает безопасность всех типов, что делает метод * чрезвычайно трудным для использования правильно, а второй не может обеспечить ограничение что только одна из каждой пары объектов должна иметь значение (а также эффективное значение «null» никогда не может быть допустимым значением, что может быть неправильным предположением.) Оно также очень слабо масштабируется * с количеством возможных типов для каждого параметра и будет еще более запутанным для вызывающего, если параметры типа для каждого параметра не совпадают. – Servy

0

См коды ниже:

interface IDo { 
    void DoSomething(); 
} 

class MyFile : File, IDo { 
    void DoSomething() { 
     // blah blah 
    } 
} 

class MyJObject : JObject, IDo { 
    void DoSomething() { 
     // blah blah 
    } 
} 

public static void MyMethod<T1, T2>(T1 one, T2 two) 
    where T1 : IDo 
    where T2 : IDo 
{ 
    one.DoSomething(); 
    two.DoSomething(); 
} 

Если файл и/или JObject запечатаны, а затем использовать сдерживание вместо наследования :

class MyFile : IDo { 
    File file; 
    void DoSomething() { 
     // blah blah 
    } 
} 

class MyJObject : IDo { 
    JObject jObject; 
    void DoSomething() { 
     // blah blah 
    } 
} 
Смежные вопросы