简体   繁体   中英

dynamic AND optional method parameters

So C# supports optional parameters:

void MethodName(string param = "optional!") { }

And dynamic method params:

void MethodName(dynamic param) { }

But unfortunately, you cannot use them together (optional param values must be constants):

void MethodName(dynamic param = new { p1 = "", p2 = 0M }) { }

I have used things like this in the past:

void GenericAnonHandler<T>(IEnumerable<T> param)
{
    foreach(T item in param)
    {
        var typedItem = Cast(item, new { p1 = "", p2 = 0M });
        var p2 = typedItem.p2; // Hark, IntelliSense!
    }
}    
static T Cast<T>(object obj, T type)
{
    return (T)obj;
}
void CallingMethod()
{
    var list1 = new List<ThisType>() { ... };
    var list2 = new List<ThatType>() { ... };

    var anon1 = list1
        .Select(x => 
            new { p1 = x.sPropName, p2 = x.dPropName });

    var anon2 = list2
        .Select(x => 
            new { p1 = x.sPropName2, p2 = x.dPropName2 });

     var both = anon1.Concat(anon2);

     GenericAnonHandler(both);
}

but that's a lot of extra work and type-specific coding where a new class or just using a dynamic would be easier, as long as you KNOW what the dynamic type is supposed to be.. but dynamics don't give IntelliSense (and reasonably so).

I would prefer to use an interface, but cannot because the source types (in this ex: ThisType, ThatType) have different property names, and I do not have control over them (3rd-party assembly).

They ARE, however, partial classes, so I could create an interface with the signature of the anonymous type with unified property names AND the different property names, implement the interface in a partial, create dummy properties for the missing values from the 'other' type, and then pull the values from the respective properties depending on the type..

..but that is also too much work .. especially if i'm going through all the effort to create 3 new items (interface, 2 partials). It would be simpler to create the anon type as a real class and translate it from the 2 previous types.

All that is to ask if there are any clever ways to accomplish what I'm wanting; optional dynamic parameters that allow intellisense to work?

I know this is a goofy question .. and basically amounts to: how can I define a class without actually defining it .. just wondering if there are any wizards out there with tricks up their sleeves other than the Cast(T,Type) route :)

The answer here is: don't do this.

A short definition suffices: struct StrDec { public string p1; public decimal p2; } struct StrDec { public string p1; public decimal p2; }

Feel free to use lots of these small definitions; preferably with names that actually document what they represent. You'll get compile time checking, intellisense, and your code will be more readable as this is a form of documentation. You might also instead use a Tuple , though I find they make your code less readable, particularly if they need to go in a heavily nested generic argument specification. Nevertheless, tuples are still better than a dynamics+anonymous type hack.

A good starting point would be...

  • Never use dynamic : this is a feature of last resort.
  • Never use optional arguments: this too is a feature of last resort.

dynamic undermines the type systems and essentially means you get the bad parts of a statically typed language (wordy) without the good parts (fast, reliable, self-documenting, intellisense). It's rarely worth it.

Optional arguments are bad because they break encapsulability, which is structured programming 101 since the 1970s - wrapping optional arguments implies repeating argument specifications and repeating default values. Additionally there is a minor technical limitation concerning the fact that they're resolved by the caller, not the callee, which won't bite you unless you deploy dll's in a fairly unusual fashion (this limitation matters for large libraries such as .NET itself). Instead, consider (once again) small, one-use structs .

I guess I would chuck the dynamic and optional part, does not seem like you really need it here. Create a new type (class or struct, depending upon your needs):

public class ThirdType
{
    public string P1 { get; set; }
    public decimal P2 { get; set ; }

    // you may want to add constructors
}

Then create some mapper methods (extension methods will do fine)

public static class MapperExtensions 
{
    public static ThirdType ToThirdType(this ThisType obj)
    {
        return new ThirdType() { P1 = obj.sPropName, P2 = obj.dPropName };
    }

    public static ThirdType ToThirdType(this ThatType obj)
    {
        return new ThirdType() { P1 = obj.sPropName2, P2 = obj.dPropName2 };
    }
}

Now whenever you call your method,

void MethodName(ThirdType param)
{
    // A.) do this...
    if (param == null)
        param = new ThirdType() { P1 = "", P2 = 0M };

}

// ... or B.) create an overload (preferable to me)
void MethodName()
{
   MethodName(new ThirdType() { P1 = "", P2 = 0M });
}

just use the extension methods. The code gets much clearer this way I think.

private void ExecuteForBoth(ThisType obj1, ThatType obj2) // dummy method, just for illustration
{
    MethodName(obj1.ToThirdType());
    MethodName(obj2.ToThirdType());
}

I wrote this without intellisense, sorry for typos.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM