Also is there a way to use run-time values for optional method parameters?
Optional parameters are required to be constants because they are written out as values of an attribute. Hence they inherit all of the restrictions that an attribute value has.
There is no way to directly encode a runtime value. However you can get close with the following pattern
public void MyApi(SomeType type = null) {
type = type ?? new SomeType();
...
}
Optional parameters are compiled into the assembly and as such (just like anything that is designated as const) they must be a compile-time constant.
And no, you cannot use execution-time values as optional parameters.
Optional parameters are determined at compile time, and substituted into the method if you call a method with too few parameters. They are handled via adding an attribute to the parameter in the method's IL.
As such, they need to be fully resolved at compile time (both for creation, since they're an attribute, but also when used). There is no way to use runtime values for optional method parameters.
Related
I have a ComVisible COM class written in C#. I want to call it from another C# bit of code using COM and pass the default value for the parameter. I can call plenty of other methods without default arguments.
This is the best I can come up with. The first two lines work for all my other methods.
Type mytype = Type.GetTypeFromProgID("MyType");
dynamic myinstance = Activator.CreateInstance(mytype);
object missingValue = System.Reflection.Missing.Value;
myinstance.generatecsvdocument("mystring", ref missingValue);
My method looks like this:
public void generatecsvdocument(string mystring, string rowseperator = "\n")
When I run it I get the error:
The best overloaded method match for 'generatecsvdocument(string,
string)' has some invalid arguments
object missingValue = System.Reflection.Missing.Value;
That cannot work here. It is only valid for a COM method that takes a VARIANT as an argument. Looks like object or dynamic in C#. A very different kind of default argument mechanism than C# supports, it is the callee that determines the default value. In C# it is the caller that determines it, the C# compiler uses metadata to know that default.
Missing.Value turns in a variant of type vtError with the value DISP_E_PARAMNOTFOUND at runtime. Signalling the COM method to use the default value. Not actually that commonly used, usually only implemented in COM servers that support scripting languages. Office Automation is the most common example, probably what inspired you to try this.
But no, your argument is string, not a variant. There is no way to discover the default either when you use late binding, implicit is that you don't know anything about the default value stored in metadata. Otherwise the reason that the vtError mechanism exists, scripting languages have the same problem. The only real way to get ahead is to rewrite the method and test for a null argument, substituting "\n" if that's the case.
I would like to set a const string from Settings.
In case I would like to change in the future the program language, it is quite easy;
Just have to modify the appropriate settings!
When trying this:
private const string constString =
"-&" + Properties.Settings.Default.constStringText;
I get this error:
The property or indexer 'Properties.Settings.Default'
cannot be used in this context because it lacks the get accessor.
Any idea?
Since you intend to use this as the default value for an optional method argument, that is:
public void Foo(string something = constString)
{
//do something
}
This constString must be a compile-time constant. From the MSDN page for "Named and Optional Arguments":
A default value must be one of the following types of expressions:
a constant expression;
an expression of the form new ValType(), where ValType is a value type, such as an enum or a struct;
an expression of the form default(ValType), where ValType is a value type.
As such, there really is no way to read a value from a configuration file at runtime then use it for an optional argument.
One workaround would be to instead declare your constString as a readonly field:
private readonly string constString =
"-&" + Properties.Settings.Default.constStringText;
Then make your optional argument required and create a wrapping overload for your method that doesn't take that parameter. That overload in turn calls the old method with the default value resolved at runtime:
public void Foo(string something) //no longer optional
{
//do something
}
public void Foo()
{
Foo(constString); //calls the other overload with the default value
}
This error is in 99% of cases related to a wrong namespace.
You've probably generated some class in which you are using Properties.Settings.Default
Solution: check namespace in that class file. It must be the same as the name of the tag in App.config (Web.config) where that particular setting is stored.
While Chris Sinclair’s answer (a const member must have value type and be evaluable at compile-time, so use readonly) is correct, I should like to explain these restrictions and answer the implicit question what the compiler message means, albeit with the disappointing remark that seems to be simply an error in the compiler.
The error message
The property or indexer 'Properties.Settings.Default' cannot be used in this context because it lacks the get accessor.
This message suggests that the expression Properties.Settings.Default could be made acceptable by adding a getter (and maybe something else) — as far as I know, that is simply wrong. After all, on the one hand, as the asker assured us1, there was a getter, and on the other, as Chris explains, the reason the expression is invalid is that is not evaluable at compile-time, and never can be, given that it depends on the run-time configuration.
Presumably this message is intended for other situations, and has been used here by mistake.
1 I have also seen this, in MSVS 2013, when a default parameter value referred to a property which did have a getter – but at least it also reported “Default parameter value for '<parname>' must be a compile-time constant”.
The restriction to value types
The restriction of const members and default parameter values to value types (as at the C# 5.0 Language Specification, 2012) appears to be not entirely inevitable, but an understandable consequence of other language design decisions.
A reference type can have a constructor evaluable at compile-time, but this is not supported ; perhaps this is because the language offers no way of indicating that a referenced object is immutable, nor even the concept of immutability of reference type objects. (Remember: an immutable reference to an object need not be a reference to an immutable object!)
Delegate values referring to static methods can also be considered fully determined at compile-time, as would those bound to immutable objects, were that concept supported.
Immutable arrays as constant values sound fairly easy to support.
Constant members¹ and (I believe)² default parameter values are specified to be part of the ‘interface’ of the class, in the sense that their values are to be determined at compile time and hard-coded into generated code using that class.
Resolution of the problem
You can use ( static ) readonly constString instead of const constString, to make clear that while the value will not change, it is not determined until run-time, at class or object initialisation (as in Chris’s answer).
If you want to use the expression as a default value for an optional parameter, it must be a true compile-time constant, leaving you two possibilities:
Declare an overload, as in Chris’s answer, e.g.:
Foo() { Foo(Default); } Foo(string s) { Bar(s); }.
This will often be the simpler solution, but could clutter your code and interface if you have many such parameters and thus many overloads, all with documentation comments.
Use null as a convention for the default, and interpret that in your method:
Foo(string s = null) { Bar(s != null ? s : Default); }.
This obviously only works if null is not a supported parameter to Foo(string) and should definitely be clarified in documentation comments.
Maybe: apply the Optional attribute, as in this question: behaviour of Optional attribute:
Foo([Optional] string s) { Bar(etc.?); } .
I have not used or studied this – the documentation seemed rather sparse – but it seems tricky and to yield no more than default = null, unless perhaps null is a supported argument to Foo(string).
References
¹ Language Specification 5.0 §10.5.2.2 10.5.2.2 Versioning of constants and static readonly fields
² I recall reading this, but have not found it in the Language Specification.
I recently encountered this scenario, and while searching for a solution I stumbled on this page. Based on the example above, I was able to resolve my issue like this:
private static readonly String ConStr
= string.Format("Data Source={0};Initial Catalog={1}; User ID={2}; Password={3};",
Properties.Settings.Default.DataSource,
Properties.Settings.Default.Catalog,
Properties.Settings.Default.UserID,
Properties.Settings.Default.Password);
Just wanted to share.
I have a constructor with optional parameters. I would like to have an expression to invoke that constructor without providing the optional arguments (I mean let the object be constructed with default values of the parameters).
I read here An expression tree may not contain a call or invocation that uses optional arguments that this is not possible.
I mean
var ctorInfo = getIt;
var f = Expression.Lambda<Func<T>>(Expression.New(ctorInfo)).Compile();
fails with System.TypeInitializationException.
Alright, I will pass the default values. But how do I get the default values of the parameters?
ctorInfo.GetParameters().Select(??
Motive: Learning purpose, no real world application.
Edit: Edited out expression-tree tag since its not in the context of building expressions, valid in general too.
According to the documentation for ParameterInfo.RawDefaultValue:
ctorInfo.GetParameters().Select( p => p.RawDefaultValue );
Hope it helps
EDIT: Corrected property because:
This property [DefaultValue] is used only in the execution context. In the
reflection-only context, use the RawDefaultValue property instead.
An expression tree may not contain a call or invocation that uses
optional arguments
return this.RedirectToAction<MerchantController>(x => x.Edit(merchantId));
Where edit had a second, nullable argument.
Why is this?
Had the same message when trying to use Mock.setup to mock a method with multiple default parameters. I just had to add the additional parameters in the lambda.
void someMethod(string arg1 = "", string arg2 = "")
mockedObject.Setup(x => x.someMethod(It.IsAny<string>(), It.IsAny<string>()))
The underlying expression tree API does not support optional arguments.
For IL-compiled code the C# compiler inserts the default values at compile time (hard-coded), because the CLR does not support calling methods with optional arguments either when the arguments are not provided explicitly.
Error: 'an exception tree may not contain a call or invocation that uses option arguments'
Why: Because you are not providing the optional parameters when calling the method. Mainly you get this with .net core when using IAsyncProxy service object.
Fix: Pass all the optional parameters value, you may use default value if you.
I dealt with it by adding the optional parameter with a value . It worked. The same thing happened when I tried mocking while doing a setup .
You might want to test that a method that has default parameters is called without any argument passed, in that case:
myMock.someMethod(default,default)
can work
I've already read the MSDN article about it. It seems internally it is the way c# sets which is the function that is going to work as indexer(am I right?). Now, I've seen the following example:
[DefaultMemberAttribute("Main")]
public class Program {
public static void Main() {
...
}
}
Now, I don't get it what it means.
Thanks all. But I still can't get its usefulness, apart from the indexer thing. When are we going to call InvokeMember?
No, the DefaultMemberAttribute is used by languages such as VB.NET to find out the member that is acted on by default if no member is referenced from an object, i.e. the member invoked by InvokeMember. This is often used in conjunction with indexers, as you noted, but it is not used by C# directly (unless you use InvokeMember explicitly).
However, for the benefit of other .NET languages, C# does emit the DefaultMemberAttribute for the indexer of a class (if it has one), as indicated by MSDN:
The C# compiler emits the
DefaultMemberAttribute on any type
containing an indexer. In C# it is an
error to manually attribute a type
with the DefaultMemberAttribute if the
type also declares an indexer.
I think MSDN confuses things by referring to indexers a lot in the remarks but then giving an example that does not use an indexer. To clarify, the default member can be anything, but C# gives special behavior for indexers by emitting the attribute for you (if an indexer exists) to the exception of all other use cases.
I personally have never used it, but as far as I can tell you are defining the default method to be invoked when calling InvokeMember. So, using the code snippet you provided if I was to say:
Program prog = new Program();
typeof(Program).InvokeMember("", null, null, prog, null);
Because I left the first argument empty of the InvokeMember call it would use the attribute to determine what the default member is of your class, in your case it is Main.
The DefaultMemberAttribute attribute defines the default member to be called on a when InvokeMember is called with an empty string as the first argument.
If you read the MSDN docs for InvokeMember, it explicitly says:
Parameters
name
Type: System.String
The String containing the name of the constructor, method, property, or field member to invoke.
-or-
An empty string ("") to invoke the default member.
The default member will be the one declared by the DefaultMemberAttribute attribute.