I am having more problems with my converting of vb.net to c#.net.
I have some files in vb.net that have "Option Strict Off" to allow for bad programming. When I convert to c# I use "dynamic" until I come back and fix problems, and this works in all cases
But now I have this code:
Public Class ContractResults
'Big class definition
Public Shared Sub CleanCache()
'Code here
End Sub
End Class
And in a file with Option Strict Off:
Public Sub VerifyResults(result as Object)
'Here, result is normally ContractResults
'first it check for that then call this:
result.CleanCache()
End Sub
In c# I use "dynamic", but a runtime error pops up when I call "static" method with dynamic reference. In vb.net, I can called "shared" sub from instance, but in c# this is not allowed
Exception:
"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException"
"Member 'ContractTypes.ContractResults.CleanCache()' cannot be accessed with an instance reference; qualify it with a type name instead"
It seems I must convert code to use actual type, but then this means much rewriting of more parts. Is anyone able to show another way?
I want to make sure you do not think I can use
(result as ContractResults).CleanCache();
Because all types that may be passed in have "CleanCache()" method, but do not inherits from anything the same other than "Object". There are many types (30!) that have this "static" method and so that is why it uses Option Strict Off
You could use reflection:
result.GetType().InvokeMember("CleanCache", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, new object[0]);
(untested)
As the error suggests, you have to call a static method from the class, not an instance:
ContractResults.CleanCache();
You could put your 30 types into a switch statement, but this would be ugly.
I'm sorry to say this, but the original decision to turn Option Strict Off was a bad one. Somehow I doubt that the code that was originally written does what you think it's doing anyway.
It's time to just go ahead and fix that problem.
Related
I am new to C#. I have read that the use of the keyword dynamic is somewhat controversial, and that other methods may be preferred depending on the use case. I want to make sure that my use of dynamic is appropriate for the language and the case. I'm working with this code:
public void myMethod(Action<T> myFunc) {
dynamic arg = "example"; // a string
myFunc(arg);
}
I don't know what the type T will be until runtime, and so I thought that dynamic would be useful. In the event that T is a string, I want to invoke myFunc with that argument. Is using dynamic the best way to do this in C#?
edit: To give more context, if T is string, then I want to pass arg to myFunc. If I don't use dynamic, I get an error that "cannot convert from 'string' to 'T'". Using dynamic solves this problem, I'm just not sure if it's the best way to solve it.
No. That is not an appropriate usage. You code will fail at runtime if T is anything other than string. That is the thing with dynamic, it turns of compiler checks, it does not mean your code will run, only that compiler errors turn into runtime errors. Dynamic is intended for interoperability with dynamic languages, or COM, where you are forced to cast objects at runtime anyway, so dynamic just makes it easier, without loosing any actual safety.
If you want to give myFunc a string, declare it as Action<string>. If you want to create a new object and give it to myFunc, add a new() restriction, i.e.
public void myMethod(Action<T> myFunc) where T : new(){
myFunc(new T());
}
If you don't know how to construct T, let the caller give it to you:
public void myMethod(Action<T> myFunc, T value) {
myFunc(value);
}
You can also use Func<T> to give your method a delegate that constructs values on demand.
Also Action<T> would be called a "generic delegate". Even if c++ templates and c# generics are used for a similar purpose, they work in a very different way. In effect c# generics is more restrictive, but also makes compiling much faster and easier since you do not have to generate code for each specialization until it is jitted.
Is there an equivalent for the C# 4 'dynamic' keyword when using type safe VB.NET, i.e. with Option Strict On?
The equivalent is Object in VB.NET but with Option Strict Off. With Option Strict On there's no equivalent. Put another way the dynamic keyword brings Option Strict Off equivalent functionality to C#.
VB.NET always had the "dynamic" feature built in, originally called late binding. This syntax was supported forever:
Dim obj = new SomeComClass()
obj.DoSomething()
Worked on code implemented in .NET and COM, the latter being the most common use. The dynamic keyword in C# gave it that same capability. It did get changed in VB.NET version 10 however, it is now using the DLR as well. Which adds support for dynamic binding to language implementations like Python and Ruby.
The syntax is exactly the same, use the Dim keyword without As. You will however have to use Option Strict Off, Option Infer On can soften that blow a bit. It does show that C# using a specific keyword to signal dynamic binding was a pretty good move. Afaik all requests to do so in VB.NET as well have as yet been considered but not planned.
If you prefer Option Strict On, then using the Partial Class keyword so you can move some of the code into another source file is probably the most effective approach.
This will demonstrate what Basic is saying about VB not having the same granularity in this as C#. I have this piece of code in C#, that uses reflection to dynamically invoke a method at runtime:
var listResult = tgtObj.GetType().GetMethod("GetSomeData").Invoke(tgtObj, null);
The reason I'm doing this is that "GetSomeData" could be any of a number of methods, each getting different data. Which method to invoke here is dependent on a string parameter passed into this object at runtime, so, value of "GetSomeData" varies at runtime.
The signature of "GetSomeData" is:
public List<SomeResultSetClass> GetSomeData()
Each one of the methods invoked returns some sort of List<T> object. Next, I'm sending the listResult object to a generic method called Export, which looks like this:
void Export<T>(List<T> exportList, string filePath, byte fileType) where T: class;
Here's where we run into a problem. Invoke returns an object of type System.Object. Of course, a List<T> is also a System.Object, but the interface exposed is the System.Object interface, not the IList interface. If I try to execute the Export method, thus:
myExportObj.Export(listResult, parms.filePath, parms.fileType);
the code fails to compile. The error is:
The type arguments for method '...Export<T>...' cannot be inferred from the usage. Try specifying the type arguments explicitly.
No thanks!! That's what I'm trying to avoid here. The problem is that the compiler can't find the IList metadata, because it's looking at the System.Object interface. Now, you can create a new List<T>, assign (List<Whatever>) listResult to it, but that defeats the purpose of dynamic invocation in the first place.
The fix is to change var to dynamic:
dynamic listResult = tgtObj.GetType().GetMethod("GetSomeData").Invoke(tgtObj, null);
Since dynamic bypasses static type checking at compile time, we don't get a compile error. Then, when the dynamic object gets passed to the Export method, the DLR (Dynamic Language Runtime) looks to see if it can implicitly cast the object to meet the requirements of the method signature. Which of course it can.
Ok, so that's how things work in C#. With VB, the line goes like this:
Dim listResult = tgtObj.GetType().GetMethod("GetSomeData").Invoke(tgtObj, Nothing)
With Option Strict On, this line upsets the compiler, as expected. With it off, it works fine. In other words, in VB, I have to turn off the type checker for the entire module that contains the line. There is no finer granularity than that.
You can turn Option Infer On and Option Strict Off and still have something very close.
There are enough ways to handle methods and properties with late binding COM objects and type safe (Option Strict On). This when using the Microsoft.VisualBasic.Interaction.CallByName and System.Type.InvokeMember methods. (Or create a separate "partial" file where Option Strict is Off).
But to handle events with late binding from VB.NET is not as straightforward as with the dynamic type in C#. You can check the "hack" for that in Dynamic Events in VB.NET.
Note that even with Option Strict on you can still use e.g. an ExpandoObject to access properties like:
Dim doc = JsonConvert.DeserializeObject(Of ExpandoObject)("{""name"":""Bob""}")
Dim lookup as IDictionary(Of String, Object) = doc
lookup("name") ' Bob
The equivalent of the c# dynamic keyword in Vb.Net with option strict on exists as a NuGet package: Dynamitey .
After install-package Dynamitey, one can write Vb.Net code as follows:
Option Strict On : Option Infer On : Option Explicit On
Imports Dynamitey
Module Module1
Public Sub Main()
Dim o = Nothing
o = "1234567890"
Console.WriteLine(Dynamic.InvokeMember(o, "Substring", 5)) ' writes 67890
End Sub
End Module
Or the slighly more readable:
Option Strict On : Option Infer On : Option Explicit On
Imports Dynamitey
Module Module1
<Extension()>
Public Function Substring(self As Object, offset As Integer) As String
Return CType(Dynamic.InvokeMember(self, "Substring", offset), String)
End Function
Public Sub Main()
Dim o = Nothing
o = "1234567890"
Console.WriteLine(Substring(o, 5)) ' writes 67890
End Sub
End Module
Tested with VS2017 and .net Framework 4.7.2 .
Yes, ExpandoObject.
Dim DObj = New System.Dynamic.ExpandoObject()
DObj.A = "abc"
DObj.B = 123
I have a huge code base and I recently made a change where I changed the type of a parameter from String to a custom class. On the next compile I got all the areas where the impact was, but areas where the input type was of type Object failed. for e.g.
String str = "32"
int i = Convert.ToInt32(str)
Now I have changed String to a new custom type lets say MyCustomClass I would now want following code to fail on next compile
MyCustomClass str = new MyCustomClass("32")
int i = Convert.ToInt32(str)
but it won't as Convert.ToInt32 also accepts type Object. Is there some way I can make a change in MyCustomClass that it's not considered Object anymore.
Please note: Convert.ToInt32 is only used for sample I have many more such functions, so please focus your suggestion/answer to question asked.
Override ToString() and IConvertible
You said in the comments that your intentions are to find places where your object, which had previously been treated as a string, and are now being treated as an object.
In these situations typically, the third-party code would call .ToString() on your object to get something which it can use.
So, Convert.ToInt32(str) is equivalent to Convert.ToInt32(str.ToString()).
If you implement ToString() and IConvertible to return whatever your old version of str looked like then it should continue to work in the same way as the old version.
Probably.
Sorry I know that is not the 100% perfect compile time answer you were looking for, but I think you also know very well that your MyCustomClass will always be considered object.
Possible compile time answer:
Write a tool which uses reflection to iterate over every class/struct/interface in every system/third-party DLL.
Output a load of CS files which contain all these same classes, but just throw NotImplementedException.
(T4 could help you do this)
Compile these classes into dummy.dll
Your .csproj now references only this one dummy.dll, instead of the real dlls.
Your project should compile fine against the dummy dll.
Look at your dummy.cs files and delete any use of object.
Re-compile... and suddenly you get a load of compile time errors showing you anywhere you are using an object.
Impliment an implicit cast from MyCustomClass to String.
public static implicit operator string(MyCustomClass str)
{
return "Legacy respresentation of str";
}
This allows the complier the choice of choosing ToInt32(Object) or ToInt32(String), and I bet it favours the later.
This way all your existing function calls will remain the same so you wont have to be concerned about third party implentation details.
(Sorry, I am not at a computer right now so I can`t test that my assumtion is correct. If you do test this, be sure to consider extension methods, as they can affect the conpilers desision making in unexpected ways)
I regularly want to get the name of an instance property of a type, when I have no instance. Currently to do this, I use the following inhouse function which interprets the Expression[Func[T, object]] parameter and returns the property name:
var str = LinqExtensions.NameOf<ClientService>(x => x.EndDate);
// Now str == "EndDate"
However it seems a shame not to use the built in nameof operator.
Unfortunately it seems that the nameof operator requires either an instance, or, to reference a static properties.
Is there a neat way to use the nameof operator instead of our in house function? For example:
nameof(ClientService.EndDate) // ClientService.EndDate not normally syntactically valid as EndDate is instance member
EDIT
I was completely wrong, the syntax nameof(ClientService.EndDate) as described actually works as is.
In the past, the documentation explicitly explained this, reading in part:
In the examples you see that you can use a type name and access an instance method name. You do not need to have an instance of the type… [emphasis mine]
This has been omitted in the current documentation. However, the examples still make this clear. Code samples such as Console.WriteLine(nameof(List<int>.Count)); // output: Count and Console.WriteLine(nameof(List<int>.Add)); // output: Add show how to use nameof to obtain the string value with the name of an instance member of a class.
I.e. you should be able to write nameof(ClientService.EndDate) and have it work, contrary to your observation in the question that this would be "not normally syntactically valid".
If you are having trouble with the syntax, please provide a good Minimal, Complete, and Verifiable code example that reliably reproduces whatever error you're getting, and provide the exact text of the error message.
Great answer by #Peter Duniho.
In case of name clashes, you can also do the following:
ClientService clientservice;
var str = nameof(clientservice.EndDate);
Not efficient, but curious enough.
I made a method to loop and clear all textbox controls in my form.
Controls.OfType<TextBox>()
.ToList()
.ForEach(tb => tb.Clear());
This works just fine, but I figured that since the first argument passed to any instance method is always a reference to the instance that I should be able to write it like this
Controls.OfType<TextBox>()
.ToList()
.ForEach(TextBox.Clear);
Unfortunately that doesn't actually work, and I don't quite understand why..
It would work if TextBox.Clear was a static method with a TextBox parameter; but instead, it's an instance method with no parameters, so the compiler can't automatically transform it to an Action<TextBox>.
Note that the CLR does support open-instance delegates (you can create one with the Delegate.CreateDelegate method), but the C# language doesn't support it.
Here's how to create an open-instance delegate that will invoke TextBox.Clear on its argument:
var action = (Action<TextBox>)Delegate.CreateDelegate(
typeof(Action<TextBox>),
null,
typeof(TextBox).GetMethod("Clear"));
The this parameter is implicit, not explicit. Foreach is expecting a method with an explicit parameter, not an implicit one.
As for why the C# language team didn't implement this feature, you'll have to ask them. They of course could have designed the language to support this, if they wanted to. There's no real point in us speculating as to why they didn't.