Mixing generic methods and extension methods - c#

I created the Class1.GetChild<T>() where T : DependencyObject extension method in lib1.dll assembly. After that, all assemblies that depends on lib1.dll failed to compile with error:
The type 'System.Windows.DependencyObject' is defined in an assemebly
that is not referenced. You must add a reference to assembly
'WindowsBase' etc...
Why dependent assemblies requires WindowsBase even if they don't use GetChild?
.
To reproduce (vs2010 .net4):
lib1.dll (references WindowsBase)
namespace lib1
{
public static class Class1
{
public static T GetChild<T>(this DependencyObject src) where T : DependencyObject
{
return default(T);
}
}
public static class Class2
{
public static int SomeExtMethod(this string src)
{
return 0;
}
}
}
lib2.dll (references lib1 but not WindowsBase)
using lib1;
class someClass
{
void someFct()
{
"foo".SomeExtMethod(); // error: The type 'System.Windows.DependencyObject'
// is defined in an assemebly that is not referenced.
// You must add a reference to assembly 'WindowsBase' etc..
}
}
.
Update:
I think there's definitly something when mixing generic methods and extension methods. I tried to demonstrate the issue in the following sample:
// lib0.dll
namespace lib0
{
public class Class0 { }
}
// lib1.dll
using lib0;
namespace lib1
{
public static class Class1
{
public static void methodA<T>() where T : Class0 { } // A
public static void methodB(Class0 e) { } // B
public static void methodC(this int src) { } // C
}
public static class Class2
{
public static void methodD(this String s) { }
}
}
// lib2.dll
using lib1;
class someClass
{
void someFct()
{
Class2.methodD(""); // always compile successfully
"".methodD(); // raise the 'must add reference to lib0' error depending on config. see details below.
}
}
A, //B, //C -> compile ok
A, B, //C -> compile ok
//A, B, C -> compile ok
A, //B, C -> raise error
A, B, C -> raise error
//A means methodA is commented. As Damien pointed out, type inference might play some role. Still curious to know the ins and outs.

Your situation has been answered by Microsoft here:
https://connect.microsoft.com/VisualStudio/feedback/details/668498/problem-with-extension-method-in-c-compiler
There are other use-cases as well independent of extension methods which produce this error wrongly.
Consider this:
Define a generic method in a type, say TP1, defined in library say LB1.
Type constrain the generic method on some type defined in some other library LB2.
Define another method in TP1.
Now in your library reference only LB1 and try to call the second method of type TP1
If you don't use TP1 but some other type defined in LB1, you do not get the error.
Also, even if one of the method of type TP1 expects a parameter of the type defined in LB2 (and you do not call this method) it does not produce this error

When one assembly depends on another assembly, the first assembly also depends on all the dependencies of the other--regardless of what is used. Assembly dependencies are effectively decoupled, another version of either assembly can be deployed after compilation, the compiler can't know that under circumstances like this one or more of the dependencies in the second assembly won't be used by the first assembly.
To solve the issue you can simply add a reference to WindowsBase.
Or, as prashanth points out, put the SomeExtMethod into a different assembly so code that uses that doesn't need to take a dependency on WindowsBase.
Update:
If you don't use anything from an assembly, you don't need any of its dependencies. But, as soon as you use one assembly, you need all the dependencies of that assembly as well. This is apparent in the way Visual Studio add references. If you add a reference to an assembly, it will copy all the dependent assemblies (not registered in the GAC) into your debug/release directories along with the assembly you added.
Update:
As to the compile error: that's the way it was written--there may be no other reason. Is it a good idea to get a compile error if you don't reference dependent assemblies? Maybe, you're likely to use something from a reference and that might use something directly from the references references--better a compile error than a deployment error.
Why not a compile error on every non-referenced secondary dependency? Again, it was written that way. Maybe an error here too would be good; but that would be a breaking change and would require really persuasive reasons.

I'm not sure this can be answered by anyone other than someone on the compiler team. I'm now thinking that it's to do with type inference - but whereas §7.6.5.1 Method Invocations talks about inference, §7.6.5.2 Extension method invocations is silent on the matter - despite the fact that inference obviously does take place when searching for applicable extension methods.
I think it's attempting some form of inference before it's performing the comparison on identifiers (which would immediately rule out the extension method since it's got the wrong name). Obviously, it can't perform any form of inference for this type if it's unable to understand the type constraints.
Hence, when you change your type constraint to just class, it now successfully passes over this method - it can infer a type parameter, but it now eliminates this extension method successfully.

When you reference another assembly, I assume the compiler needs to be able to parse any method signatures defined in that assembly, so it knows where to go to find that function if it sees a call to it.
If you replace your GetChild() function with
public static T GetChild<T>(this T src)
{
if (typeof(T) == typeof(DependencyObject)) return default(T);
else return default(T);
}
or something similar to that, it does not require you to include the reference to WindowsBase that you're running into. But if you add where T : DependencyObject to the signature, it does require it.
Effectively, you can use whatever assembly references you want in a project, so long as you don't expose them in any way. Once you expose them, then every other project which uses your library needs to be able to handle them, and thus requires those references themselves.

Maybe ILMerge would solve this problem. The idea is you create 2 dlls and merge them into one. That way you can have a single dll but reference it twice. Then way you can separate the GUI code from other code and only add the reference that you need to the particular project.

The answer is simple. It is because the method is decalre as public. This mean that it is visible to lib2.dll (in your case.) In other word you can call this method.
It also has a constrain that only classes inherited from DependencyObject can call this method. So that is the reason why you need to reference 'WindowsBase'.

Related

Returning into object from Assembly.LoadFrom() in C#

I'm trying to make an expansion setup for this game I'm developing, (not going to go into detail about), but all a single expansion will need is the 1 .dll file added into the Expansions folder I have added.
I have figured out how to access these .dll added into this folder as seen below:
Assembly ExpAssembly = Assembly.LoadFrom("Expansions/Intrique.dll");
Type myType = ExpAssembly.GetTypes()[0];
Here is an example of the class I'm trying to load:
public class Expansion: MyGame.Expansion {
public Expansion() {
//Stuff
}
public string SomeMethod()
{
return "Test";
}
}
Calling the following code runs SomeMethod() just fine
MethodInfo Method = myType.GetMethod("SomeMethod");
object myInstance = Activator.CreateInstance(myType);
MessageBox.Show(Method.Invoke(myInstance, null).ToString());
But what I want to do is be able to write Expansion expObj; and assign it by calling new Expansion() from this not-referenced .dll, but not in the library itself.
(For the purposes of this answer, I'm going to assume that your Expansion subclass is has a fully qualified name of Intrique.Expansion. I.e. the namespace is the same as the name of the DLL).
Because your main program does not reference Intrique.dll, the code in your main program cannot use the types in that DLL directly. That is, Intrique.Expansion is not a usable type in the context of the written code of your main program, though it can be used at run-time.
Taking your code example literally, the only approach likely to work given the code you have now would be to use dynamic:
dynamic myInstance = Activator.CreateInstance(myType);
myInstance.SomeMethod();
This is because SomeMethod() is declared only in Intrique.Expansion. There's not any other type you could use statically in your main program where that method is known.
If that method was instead an implementation of a member of some interface that Intrique.Expansion implements and which your main program references, or was an override of some virtual member of MyGame.Expansion (which presumably your main program references, if not actually declares), then you could cast the instance to the interface type or MyGame.Expansion respectively and call the method that way:
ISomeInterface myInstance = (ISomeInterface)Activator.CreateInstance(myType);
myInstance.SomeMethod();
or:
MyGame.Expansion myInstance = (MyGame.Expansion)Activator.CreateInstance(myType);
myInstance.SomeMethod();
Finally, given that you are trying to implement some kind of extensibility architecture, you might consider using the Managed Extensibility Framework, which is designed specifically to handle a lot of the messy parts of exactly this kind of thing.

Curious dependency resolution error

I've recently encountered a dependecy resolution error that I'm hoping someone here can explain.
I have an interface defined in a 3rdparty assembly (I3rdParty), one "common" assembly that's depends on that assembly and a "client" library that depends on the "common" assembly.
Let's call them, 3rdparty.dll, common.dll and client.dll.
The client.dll should not have a dependency to the 3rdparty.dll.
in common.dll the following was defined:
public static class Factory
{
public static object Create(I3rdParty ifc) { ... }
public static object Create(string value1, string value2, long? value3 = null) { ... }
}
One of the factory methods was used from the client.dll like:
var instance = Factory.Create("SomeValue", "SomeValue2");
At this point everything worked as expected.
Then a bool parameter was introduced to the first factory method in common.dll so it became:
public static object Create(I3rdParty ifc, bool value) { ... }
Then the build of client.dll started failing due to a missing dependency to 3rdparty.dll, e.g:
The type 'I3rdParty' is defined in an assembly that is not referenced...
I'm assuming that this has something to do with that the methods now accepts the same number of parameters (since the second Create method's third parameter defaults to null).
But I thought that it would still be able to select the correct Create method based on the type of the parameters. Can anyone explain the reason for the behavior I'm seeing?
After you added a bool parameter to the first overload, the compiler has now to check for two possible method signatures to choose the one that should be used (this is the overload resolution).
You're calling Create(string, string)
With two parameters, you have the following overloads available:
Create(I3rdParty, bool)
Create(string, string)
Obviously only the second one can match (as a string cannot be implicitly converted to bool for the second parameter), but it appears the compiler is not clever enough and has to know what exactly I3rdParty is (which means it requires the reference to the assembly that defines it), before being able to determine the (I3rdParty, bool) overload wasn't an option.

Why can't I inherit from ScrollChangedEventArgs? [duplicate]

I'm noticing the compiler error The type '...' has no constructors defined generated when I erroneously attempt to instantiate a particlar class.
It lead me to wonder how I would go about writing my own class that would precipitate this message when someone attempted to instantiate it.
So the code below, what do I need to do to MyClass?
namespace MyNamespace
{
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
}
}
class MyClass
{
MyClass()
{
}
}
}
This error (CS0143) occurs if the class only defines an internal constructor and you try to instantiate it from another assembly.
public class MyClass
{
internal MyClass()
{
}
}
Also this error could be cause if you are compiling with Framework 4 or higher and embedding the Interop Types into your managed assembly. To get rid of this error you need to turn off (No embed) the Embedded Interop Types.
Instructions to turn off embedding:
On VS2010 Solution Explorer, right click on the Interop Reference that you are using.
Select Properties and look for Embed Interop Types
Change it from True to False
You can read about Embedded Interop Types here.
Pablo
I've managed to reproduce this by:
Creating a static class in a DLL
Using ildasm to decompile it to IL
Editing the IL to remove the "abstract" and "sealed" modifiers from the class
Rebuilding the DLL with ilasm
Compiling a program which tries to create an instance of the class
If you don't remove the abstract/sealed modifiers, the C# compiler recognizes it as a static class and gives a different error message. Of course, you could start off with a "normal" type and just remove the constructors, too.
EDIT: I actually thought I hadn't submitted this, as I saw the "internal" constructor one first. However, I'll leave it now as my version makes the C# compiler correct - there's a difference between a type having no accessible constructors and genuinely having no constructors :)
I believe you would need to make the constructor of the class internal in order to have it throw this exception. I believe you'll also need the class to exist in another assembly.
namespace MyNamespace
{
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
}
}
}
namespace DifferentNamespace
{
class MyClass
{
internal MyClass()
{
}
}
}
As has been said, you can get CS0143 by trying to instantiate a class with an internal constructor from outside its assembly.
But I believe it's a compiler bug. The error generated should be CS0122:
'member' is inaccessible due to its protection level
... which is the error you get if you try to instantiate a class with only a private constructor.
CS0143 used to happen (up to C# 3.0) if you tried to call a constructor for a built-in type like Double, but in C# 4.0 that now generates CS1729:
'type' does not contain a constructor that takes 'number' arguments.
if you pass an argument
Double d = new Double(1.25);
... or no error at all if you don't pass any arguments to the constructor.
Yet another option: the code might be right, but you might work on different projects in different instances of Visual Studio, and therefore you need to build the referenced project first.

Why am I getting this definition reference error?

I am using TaskBar methods defined within the namespace Microsoft.WindowsAPICodePack.Taskbar. Specifically, I'll focus on SetProgressState for this question.
Here is the meta-definition I get when I ask for the definition of SetProgressState:
namespace Microsoft.WindowsAPICodePack.Taskbar
{
public class TaskbarManager
{
public void SetProgressState(TaskbarProgressBarState state);
public void SetProgressState(TaskbarProgressBarState state, IntPtr windowHandle);
public void SetProgressState(TaskbarProgressBarState state, System.Windows.Window window);
}
}
Obviously, I have omitted most of that class's definition just to highlight the one method's overloads.
To this point, I have been using the single-parameter overload and have had no issues. However, today I attempted to use the two-parameter overload that accepts an IntPtr as its second parameter.
When I did that, I started getting this error during build:
The type 'System.Windows.Window' is defined in an assembly that is not
referenced. You must add a reference to assembly
'PresentationFramework, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35'
So my question is why I did not get an error for using the single-parameter overload, but I do get an error for referencing one of the others (and for the wrong one)?
Edit (for addition sub-question):
I also tried the following, which made no difference:
SetProgressState(myState, (IntPtr) myWindowHandle);
I thought that by casting explicitly, I would avoid the compiler confusion in realizing the appropriate overload, but that was not the case.
According to the MSDN page on Overload Resolution, the compiler will start by selecting the potential candidates
Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way
then, the best target is selected:
If the set contains only one function member, then that function member is the best function member.
My understanding here is that the compiler doesn't even consider the 2 arguments methods when you call it with 1 argument. However, when you use the 2 arguments version, it needs information about the argument types. In this case, it needs to know what System.Windows.Window is to be able to determine which overload you want to call.
Example
Imagine you have 2 classes in separate Class Libraries
class Foo
{
}
class Bar : Foo
{
}
and 4 methods in an other library
static void Do()
{
}
static void Do(Foo foo)
{
}
static void Do(Bar bar)
{
}
static Foo Get()
{
return new Bar();
}
You reference the Methods Library and the Library Containing Foo, but not the library containing Bar.
Then, in your application, you obtain an object of type Foo from the Methods Library (it could be a Bar too, but you don't know). How is the compiler supposed to resolve an eventual call to Do() with arguments?
It can't unless it has the type information for Bar as well.
As for your subquestion, it's a result of the above plus the fact that a cast doesn't necessarily force an overload to be chosen. Let's imagine that System.Windows.Window derives from IntPtr for a moment. Casting the argument to IntPtr doesn't help the compiler resolve the overload at all (see above example).
Since the type information is not present, the compiler emits an error because it can't know for sure. Honestly, for compilers, that's a feature.
I'll expand my comments here for clarity. Your project isn't able to find System.Windows.Window. I mispoke in my comment when I said you need to put in:
using System.Windows;
To the file.
Instead, the project needs to have a reference to System.Windows. The reference you want is given to you in the error message: PresentationFramework. You will also need to include PresentationCore(a similar error will pop up telling you to add a reference to PresentationCore).
The type 'System.Windows.Window' is defined in an assembly that is not referenced. You must add a reference to assembly 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

The type '...' has no constructors defined

I'm noticing the compiler error The type '...' has no constructors defined generated when I erroneously attempt to instantiate a particlar class.
It lead me to wonder how I would go about writing my own class that would precipitate this message when someone attempted to instantiate it.
So the code below, what do I need to do to MyClass?
namespace MyNamespace
{
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
}
}
class MyClass
{
MyClass()
{
}
}
}
This error (CS0143) occurs if the class only defines an internal constructor and you try to instantiate it from another assembly.
public class MyClass
{
internal MyClass()
{
}
}
Also this error could be cause if you are compiling with Framework 4 or higher and embedding the Interop Types into your managed assembly. To get rid of this error you need to turn off (No embed) the Embedded Interop Types.
Instructions to turn off embedding:
On VS2010 Solution Explorer, right click on the Interop Reference that you are using.
Select Properties and look for Embed Interop Types
Change it from True to False
You can read about Embedded Interop Types here.
Pablo
I've managed to reproduce this by:
Creating a static class in a DLL
Using ildasm to decompile it to IL
Editing the IL to remove the "abstract" and "sealed" modifiers from the class
Rebuilding the DLL with ilasm
Compiling a program which tries to create an instance of the class
If you don't remove the abstract/sealed modifiers, the C# compiler recognizes it as a static class and gives a different error message. Of course, you could start off with a "normal" type and just remove the constructors, too.
EDIT: I actually thought I hadn't submitted this, as I saw the "internal" constructor one first. However, I'll leave it now as my version makes the C# compiler correct - there's a difference between a type having no accessible constructors and genuinely having no constructors :)
I believe you would need to make the constructor of the class internal in order to have it throw this exception. I believe you'll also need the class to exist in another assembly.
namespace MyNamespace
{
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
}
}
}
namespace DifferentNamespace
{
class MyClass
{
internal MyClass()
{
}
}
}
As has been said, you can get CS0143 by trying to instantiate a class with an internal constructor from outside its assembly.
But I believe it's a compiler bug. The error generated should be CS0122:
'member' is inaccessible due to its protection level
... which is the error you get if you try to instantiate a class with only a private constructor.
CS0143 used to happen (up to C# 3.0) if you tried to call a constructor for a built-in type like Double, but in C# 4.0 that now generates CS1729:
'type' does not contain a constructor that takes 'number' arguments.
if you pass an argument
Double d = new Double(1.25);
... or no error at all if you don't pass any arguments to the constructor.
Yet another option: the code might be right, but you might work on different projects in different instances of Visual Studio, and therefore you need to build the referenced project first.

Categories

Resources