Baffled.
class Test
{
void Main()
{
F(() => ""); // ok
F(named: () => ""); // 'T' cannot be inferred from the usage!
F<string>(() => ""); // ok
F<string>(named: () => ""); // ok
}
void F<T>(Func<T> named) { }
}
Could someone tell me why the second call to F fails to compile?
(Note that this is a significantly stripped down example, which is why it seems synthetic. In the real case I came across, there are some default parameters before 'named' and so the named parameter is required. And so, apparently is explicit specification of 'T' by the caller.)
Seems like an inadequacy in the compiler's delegate type inference...sorry I can't offer more.
Related
For example, consider the following C# code:
interface IBase { void f(int); }
interface IDerived : IBase { /* inherits f from IBase */ }
...
void SomeFunction()
{
IDerived o = ...;
o.f(5);
}
I know how to get a MethodDefinition object corresponding to SomeFunction.
I can then loop through MethodDefinition.Instructions:
var methodDef = GetMethodDefinitionOfSomeFunction();
foreach (var instruction in methodDef.Body.Instructions)
{
switch (instruction.Operand)
{
case MethodReference mr:
...
break;
}
yield return memberRef;
}
And this way I can find out that the method SomeFunction calls the function IBase.f
Now I would like to know the declared type of the object on which the function f is called, i.e. the declared type of o.
Inspecting mr.DeclaringType does not help, because it returns IBase.
This is what I have so far:
TypeReference typeRef = null;
if (instruction.OpCode == OpCodes.Callvirt)
{
// Identify the type of the object on which the call is being made.
var objInstruction = instruction;
if (instruction.Previous.OpCode == OpCodes.Tail)
{
objInstruction = instruction.Previous;
}
for (int i = mr.Parameters.Count; i >= 0; --i)
{
objInstruction = objInstruction.Previous;
}
if (objInstruction.OpCode == OpCodes.Ldloc_0 ||
objInstruction.OpCode == OpCodes.Ldloc_1 ||
objInstruction.OpCode == OpCodes.Ldloc_2 ||
objInstruction.OpCode == OpCodes.Ldloc_3)
{
var localIndex = objInstruction.OpCode.Op2 - OpCodes.Ldloc_0.Op2;
typeRef = locals[localIndex].VariableType;
}
else
{
switch (objInstruction.Operand)
{
case FieldDefinition fd:
typeRef = fd.DeclaringType;
break;
case VariableDefinition vd:
typeRef = vd.VariableType;
break;
}
}
}
where locals is methodDef.Body.Variables
But this is, of course, not enough, because the arguments to a function can be calls to other functions, like in f(g("hello")). It looks like the case above where I inspect previous instructions must repeat the actions of the virtual machine when it actually executes the code. I do not execute it, of course, but I need to recognize function calls and replace them and their arguments with their respective returns (even if placeholders). It looks like a major pain.
Is there a simpler way? Maybe there is something built-in already?
I am not aware of an easy way to achieve this.
The "easiest" way I can think of is to walk the stack and find where the reference used as the target of the call is pushed.
Basically, starting from the call instruction go back one instruction at a time taking into account how each one affects the stack; this way you can find the exact instruction that pushes the reference used as the target of the call (a long time ago I wrote something like that; you can use the code at https://github.com/lytico/db4o/blob/master/db4o.net/Db4oTool/Db4oTool/Core/StackAnalyzer.cs as inspiration).
You'll need also to consider scenarios in which the pushed reference is produced through a method/property; for example, SomeFunction().f(5). In this case you may need to evaluate that method to find out the actual type returned.
Keep in mind that you'll need to handle a lot of different cases; for example, imagine the code bellow:
class Utils
{
public static T Instantiate<T>() where T : new() => new T();
}
class SomeType
{
public void F(int i) {}
}
class Usage
{
static void Main()
{
var o = Utils.Instantiate<SomeType>();
o.F(1);
}
}
while walking the stack you'll find that o is the target of the method call; then you'll evaluate Instantiate<T>() method and will find that it returns new T() and knowing that T is SomeType in this case, that is the type you're looking for.
So the answer of Vagaus helped me come up with a working implementation.
I published it on github - https://github.com/MarkKharitonov/MonoCecilExtensions
Included many unit tests, but I am sure I missed some cases.
I am completely baffled. I am essentially trying to do foo["x"]="y" and getting the most baffling of exceptions. I actually made a minimal working example, and I had to nest Maps and Lists for the thing to break. I have no idea what's going on.
Map fixprobset(Map P) {
print(P);
if(!P.containsKey("name")) {
final foo = P["tileset"].join(" ");
P["name"] = foo;
}
if(P.containsKey("children")) {
for(var k=0; k<P["children"].length;k++) fixprobset(P["children"][k]);
}
return P;
}
void main() {
Map problemset = fixprobset({
"name": "Jingle Jangle",
"children": [
{
"tileset": ["1","2","3"]
}
]
});
print(problemset);
}
Click here and gaze in wonder on the bafflement
OK so now I understand what's going on. Objects have a runtimeType, and even though P has the unassuming type declaration Map, P.runtimeType could be e.g. Map<String,List<String>>. Once that happens, a Map can no longer store heterogeneous objects. Getting around this was quite hard for me, the type declarations Map<String,dynamic> P or Map<String,Object> P really don't help, the undesirable runtimeType is there to stay. I've filed this as a "bug" although perhaps it will be deemed a (mis)feature.
This gist has the workaround.
You problems comes from the fact that your are fighting against the type system in Dart. As you have discovered, there are something about the runtimeType which seems to be automatically assigned to some other type than dynamic.
I have made this example which works:
Map fixprobset(Map<dynamic, dynamic> P) {
print(P);
if (!P.containsKey("name")) {
final foo = (P["tileset"] as List).join(" ");
P["name"] = foo;
}
if (P.containsKey("children")) {
for (var k = 0; k < (P["children"] as List).length; k++) {
fixprobset((P["children"] as List)[k] as Map);
}
}
return P;
}
void main() {
final problemset = fixprobset(<dynamic, dynamic>{
"name": "Jingle Jangle",
"children": <dynamic>[
<dynamic, dynamic>{
"tileset": <dynamic>["1", "2", "3"]
}
]
});
print(problemset);
}
As you can see, the automatically assigned types comes from your input data which gets assigned types which makes sense at the start of the program. Your problem is then, that you don't wants this automatically assigned types but want to change them after creation. So you want to change a List<String> into a List<dynamic> but to do that, you need to create a new object since you cannot change the type of an object after its creation.
So my "solution" fixes this by setting the type at the time of the creation of the lists and maps in the input of the first call to fixprobset.
Still trying out swift, and I came across this problem (not sure if it really classifies as one)
So we have a protocol, and a structure that inherits it.
protocol ExampleProtocol {
var simpleDescription: String { get }
func adjust()
}
struct SimpleStructure : ExampleProtocol{
var simpleDescription = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
func adjust() { //I created this second method just to conform to the protocol
}
}
var b = SimpleStructure()
b.adjust() //This generates a compiler error mentioning Ambiguity (Correct)
Question is how do I call the mutating adjust() not the adjust from the protocol. i.e. I know if I declare b as a protocol and initialized it to the struct it will call adjust from protocol, but how do I call the first adjust ? or is it not possible? Or Am I using it wrongly ?
Cheers,
Your code doesn't compile, but the error is in redefining the adjust method by adding the mutating attribute - that doesn't create an overloaded version of adjust.
In my opinion this is the correct code:
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
struct SimpleStructure : ExampleProtocol{
var simpleDescription = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
which means: you have to define the adjust function as mutating in the protocol.
How do I do the following shown in Javascript in C# 4.0:
var output = doSomething(variable, function() {
// Anonymous function code
});
I'm sure I've seen this somewhere before but I cannot find any examples.
Using a lambda expression (parameterless, therefore empty parentheses), it is very simple:
var output = doSomething(variable, () => {
// Anonymous function code
});
In C# 2.0, the syntax was a bit longer:
SomeType output = doSomething(variable, delegate {
// Anonymous function code
});
You'll want to look in to Lambda Expressions though it's not QUITE like JavaScript because C# works quite a bit differently. You may also want to check out delegates.
Example Code:
namespace Test {
class Tests {
delegate string MyDelegate();
public void Main(string[] args) {
var output = doSomething("test1", () => { return "test2";} );
}
public string doSomething(string test, MyDelegate d) {
return test + d();
}
}
}
var output = (x) => {
// Anonymous function code
};
I'm trying to recompile older code in latest Visual Studio (2008) and code that worked previously now fails to compile. One of the problems is due to overloaded operators for my class. below there is simplified class to demonstrate the problem. If I remove casting operators for int and char* then it works fine. So one of the ways to fix my issue is to replace them with procedures to_char and to_int and use them instead but it will require a lot of changes in the code (that class is heavily used). There must be some better, smarter way to fix it. any help is greatly appreciated :-)
class test
{
public:
test();
test(char* s2);
test(int num);
test(test &source);
~test();
operator char*();
operator int();
};
test::test() {
}
test::test(char* s2) {
}
test::test(int num) {
}
test::test(test &source) {
}
test::operator char*() {
}
test::operator int() {
}
test test_proc() {
test aa;
return aa;
}
int test_proc2(test aa)
{
return 0;
}
int main()
{
test_proc2(test_proc());
}
//test.cpp(60) : error C2664: 'test_proc2' : cannot convert parameter 1 from 'test' to 'test'
// Cannot copy construct class 'test' due to ambiguous copy constructors or no available copy constructor
Try changing
test(test &source);
to
test(const test &source);
The issue is that the test_proc call returns a temporary test object, which can be passed to a function that accepts a const reference, but not a plain reference.