Why isn't the more derived extension method called? [duplicate] - c#

This question already has answers here:
calling an overloaded method in generic method
(5 answers)
Closed 2 years ago.
In the code below I'm (wrongfully apparently) expecting that method of the more specific (derived if you will) type, to be bound/called:
using System;
public class Program
{
public static int integer = 52;
public static Program program = new Program();
public static void Main()
{
TestReturn(program); // this works as expected all the way
TestReturn(integer); // 1. this not quite...
}
public static T TestReturn<T>(T t) // 2. TestReturn<Int32> all good...
{
Console.WriteLine("In TestReturn<" + typeof(T) + ">");
return (T)Extensions.Undefined(t); // 3. wrong call
}
}
public static class Extensions
{
public static object Undefined(this object t) // 4. this is called, instead of (5)
{
Console.WriteLine("In Undefined(obj)");
return null;
}
public static int Undefined(this int b) // 5. this is expected to be called
{
Console.WriteLine("In Undefined(int)");
return int.MinValue;
}
}
Output:
In TestReturn<Program>
In Undefined(obj)
In TestReturn<System.Int32>
In Undefined(obj)
Run-time exception (line 17): Object reference not set to an instance of an object.
Can someone tell why is this happening and how to do it in order to work as I intended?

Taking from the C# Programming guide:
The basic definition of extension method says:
The intermediate language (IL) generated by the compiler translates your code into a call on the static method.
How the extension methods are binded at run time:
An extension method with the same name and signature as an interface or class method will never be called. At compile time, extension methods always have lower priority than instance methods defined in the type itself.
When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. If no match is found, it will search for any extension methods that are defined for the type, and bind to the first extension method that it finds.
Extending predefined types can be difficult with struct types because they're passed by value to methods. That means any changes to the struct are made to a copy of the struct. Those changes aren't visible once the extension method exits.
As per documentation, and also mentioned in the comments by #UnholySheep and #Alexander Høst, at the compile time of your Program class, the compiler choses the more generic type to suffice the general approach of your "T" type. In other words, the extension method covering an "object" is chosen by the compiler as "T" might be anything.
The proof of concept it that the following code won't compile:
namespace ConsoleForEverything
{
public class Program
{
public static int integer = 52;
public static Program program = new Program();
public static void Main()
{
TestReturn(integer); // 1. this not quite...
}
public static T TestReturn<T>(T t) // 2. TestReturn<Int32> all good...
{
Console.WriteLine("In TestReturn<" + typeof(T) + ">");
return (T)Extensions.Undefined(t); // 3. wrong call
}
}
public static class Extensions
{
public static int Undefined(this int b) // 5. this is expected to be called
{
Console.WriteLine("In Undefined(int)");
return int.MinValue;
}
}
}

Related

Why does this function call resolve to the base class method, casting the int to a double?

Why does this program print "abc from B" instead of "abc from A"? My intuition says that it should resolve the method to the more specific type parameter (which is how I've always used overloading).
public class Program
{
public static void Main(string[] args)
{
int i = 5;
B b = new B();
b.Method1(i);
Console.ReadLine();
}
}
class A
{
public void Method1(int q)
{
Console.WriteLine("abc from A");
}
}
class B : A
{
public void Method1(double p)
{
Console.WriteLine("abc from B");
}
}
Overload resolution doesn't work across types.
This is due to one crucial step in determining the set of candidate methods for overload resolution, as stated in the language spec:
The set of candidate methods is reduced to contain only methods from the most derived types: For each method C.F in the set, where C is the type in which the method F is declared, all methods declared in a base type of C are removed from the set.
Basically, the A.Method1 isn't even considered for overload resolution.
There are a few other steps to this, but the spec nicely summarises the process:
To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform type inference and overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected.
Interestingly, your intuition that A.Method1 should be called because there is an identity conversion from int to int is how it works in Java :)
Because you called it from a variable of the B type. If you cast b as an A, you'd get the first result. See it here:
https://dotnetfiddle.net/TAYfJX
If you want it to do overloading based on argument type, put both versions of the method in the same type.

Why are the arguments to a Conditional method always type-checked?

In the event that a Conditional method is compiled away, the arguments to each invocation are still type-checked at compile time. What is the motivation for this? Example:
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int x = 2;
string st = "";
// this invocation compiles fine
ConditionalMethod(x, st);
// this invocation won't compile
ConditionalMethod(st, x);
}
[Conditional("condition")]
public static void ConditionalMethod(int x, string st) { }
}
}
To be clear, the conditional symbol "condition" is not defined in this context, so the method invocations are omitted from the MSIL resulting from compilation. This is on par with the spec defined here, so no surprises there. Imagine a more complex scenario:
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ConditionalMethod(new Bar());
}
[Conditional("condition")]
public static void ConditionalMethod(Foo foo) { }
public class Foo { }
#if condition
public class Bar : Foo { }
#else
public class Bar { }
#endif
}
}
Invocations of 'ConditionalMethod' will only be included in the resulting compilation when the conditional symbol "condition" is defined. In that scenario, however, Bar CAN in fact be upcast to Foo. If the compiler knows that invocations to 'ConditionalMethod' will be compiled away, shouldn't it also be aware that in the event that we care about invocations of this method, this code will be legit? Yes, this is a contrived and hair-splitting example, but it helps illustrate my question. I'm asking out of benign curiosity, as this has irked me for quite some time now. Please help, Jon Skeet. :)
Imagine this code
class Program
{
static void Main(string[] args)
{
int x = 2;
string st = "";
// this invocation compiles fine
Blah(x, st);
// this invocation won't compile
Blah(st, x);
}
[Conditional("condition")]
public static void Blah(int x, string st) { }
public static void Blah (string st, int x, int y) { }
public static void Blahh(string st, int x) { }
}
A method's signature is a critical part of associating a call to the method that should be called. That association must be made before it is known whether the Conditional attribute applies. In the example above, the call does not match any of the methods. The compiler would have to make a leap of faith to guess that you meant the Blah(int,string). But it would be a guess, because the the signature doesn't match (the argument types are in the wrong order). Blah(string,int,int) is also pretty close - you just forgot an argument. And Blahh(string,int) is close too - you just made a typo in the name.
For a similar example, see this blog post by Eric Lippert (who knows this stuff).
Therefore the arguments must be well-defined at the point of the call,
even if the call is going to be removed. In fact, the call cannot be
removed unless the arguments are extant!
and later
The effect of a conditional compilation directive happens at lex time;
anything that is inside a removed #if block is treated by the lexer as
a comment. It’s like you simply deleted the whole contents of the
block and replaced it with whitespace. But removal of call sites
depending on conditional attributes happens at semantic analysis time;
everything necessary to perform that semantic analysis must be
present.

The call is ambiguous between the following methods or properties (one static and one non-static)

Why am I not allowed to have a static and non-static methods with the same signature?
Let's say I have a class like this
public class TestClass
{
public object thing { get; set; }
public TestClass()
{
}
public TestClass(object thing)
{
this.thing = thing;
}
public static TestClass ConvertTestClass(object thing)
{
return new TestClass(thing);
}
public TestClass ConvertTestClass(object thing)
{
this.thing = thing;
return this;
}
}
and I try to use it like this
public class SomeOtherClass
{
public SomeOtherClass()
{
TestClass tc = TestClass.ConvertTestClass(new object());
TestClass tc2 = new TestClass();
tc2.ConvertTestClass(new object());
}
}
I get the following errors on TestClass.ConvertTestClass(new object());
The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'
and these errors on tc2.ConvertTestClass(new object());
The call is ambiguous between the following methods or properties: 'TestClass.ConvertTestClass(object)' and 'TestClass.ConvertTestClass(object)'
Member 'TestClass.ConvertTestClass(object)' cannot be accessed with an instance reference; qualify it with a type name instead
Can the compiler really not tell the difference between the static and non static versions of that method or am I missing something here?
I am not using ReSharper (which seemed to be the root of a similar problem in other questions).
Its giving you an error, so its a safe bet that the compiler can't, or won't, discern between the two methods.
Its probably a bad idea to do this kind of overload anyways, as it's unclear which method you are intending to invoke, but if that isn't enough, the C# 5 specification defines a method signature like this (Section 3.6):
The signature of a method consists of the name of the method, the
number of type parameters and the type and kind (value, reference, or
output) of each of its formal parameters, considered in the order left
to right. For these purposes, any type parameter of the method that
occurs in the type of a formal parameter is identified not by its
name, but by its ordinal position in the type argument list of the
method. The signature of a method specifically does not include the
return type, the params modifier that may be specified for the
right-most parameter, nor the optional type parameter constraints.
It doesn't explicitly mention static, but it also doesn't include it as part of the definition of a "signature". Thus, its fair to assume that by the spec, a method overload cannot differ only in the fact that it is static or instanced.
I'd write this as a comment however it's easier to make this point in a proper editor.
I think you're only thinking about the logic of calling methods on the class externally i.e. from another class. Within the class methods with the same signature only differing by static doesn't make any sense. e.g you have a class with two methods as follows
public class MyClass
{
public static void HellowWorld()
{
Console.WriteLine("Hello World!");
}
public void HellowWorld()
{
Console.WriteLine("Howdy World!");
}
public void Greet()
{
HellowWorld();
}
}
When compiling you'll see as long as one of the methods is commented out it compiles without errors. You should be able to alternate the commented out method and compile the class succesfully. Indicating there's no way of differentiating which method should be called within the scope of the class.
You could argue that within the class you should be forced to use the same syntax to call a static method as you do externally e.g.
MyClass.HelloWorld();
However, this would defy scoping logic used throughout C#, why should you need to specify the class name within a class? I think such a change would also create ambiguity where the was none, and to do so now would of course break a lot of code out there.
I think the compiler logic as it is makes perfect sense.

How to extend Int32 to add FromSomething? [duplicate]

I'm a fan of extension methods in C#, but haven't had any success adding an extension method to a static class, such as Console.
For example, if I want to add an extension to Console, called 'WriteBlueLine', so that I can go:
Console.WriteBlueLine("This text is blue");
I tried this by adding a local, public static method, with Console as a 'this' parameter... but no dice!
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
This didn't add a 'WriteBlueLine' method to Console... am I doing it wrong? Or asking for the impossible?
No. Extension methods require an instance variable (value) for an object. You can however, write a static wrapper around the ConfigurationManager interface. If you implement the wrapper, you don't need an extension method since you can just add the method directly.
public static class ConfigurationManagerWrapper
{
public static ConfigurationSection GetSection( string name )
{
return ConfigurationManager.GetSection( name );
}
.....
public static ConfigurationSection GetWidgetSection()
{
return GetSection( "widgets" );
}
}
Can you add static extensions to classes in C#? No but you can do this:
public static class Extensions
{
public static T Create<T>(this T #this)
where T : class, new()
{
return Utility<T>.Create();
}
}
public static class Utility<T>
where T : class, new()
{
static Utility()
{
Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile();
}
public static Func<T> Create { get; private set; }
}
Here's how it works. While you can't technically write static extension methods, instead this code exploits a loophole in extension methods. That loophole being that you can call extension methods on null objects without getting the null exception (unless you access anything via #this).
So here's how you would use this:
var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create()
// or
DataSet ds2 = null;
ds2 = ds2.Create();
// using some of the techniques above you could have this:
(null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)
Now WHY did I pick calling the default constructor as an example, and AND why don't I just return new T() in the first code snippet without doing all of that Expression garbage?
Well todays your lucky day because you get a 2fer. As any advanced .NET developer knows, new T() is slow because it generates a call to System.Activator which uses reflection to get the default constructor before calling it. Damn you Microsoft!
However my code calls the default constructor of the object directly.
Static extensions would be better than this but desperate times call for desperate measures.
It's not possible.
And yes, I think MS made a mistake here.
Their decision does not make sense and forces programmers to write (as described above) a pointless wrapper class.
Here is a good example: Trying to extend static MS Unit testing class Assert: I want 1 more Assert method AreEqual(x1,x2).
The only way to do this is to point to different classes or write a wrapper around 100s of different Assert methods. Why!?
If the decision was being made to allow extensions of instances, I see no logical reason to not allow static extensions. The arguments about sectioning libraries does not stand up once instances can be extended.
I stumbled upon this thread while trying to find an answer to the same question the OP had. I didn't find the answer I wanted, but I ended up doing this.
public static class Helpers
{
public static void WriteLine(this ConsoleColor color, string text)
{
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ResetColor();
}
}
And I use it like this:
ConsoleColor.Cyan.WriteLine("voilà");
As of C#7 this isn't supported. There are however discussions about integrating something like that in C#8 and proposals worth supporting.
Maybe you could add a static class with your custom namespace and the same class name:
using CLRConsole = System.Console;
namespace ExtensionMethodsDemo
{
public static class Console
{
public static void WriteLine(string value)
{
CLRConsole.WriteLine(value);
}
public static void WriteBlueLine(string value)
{
System.ConsoleColor currentColor = CLRConsole.ForegroundColor;
CLRConsole.ForegroundColor = System.ConsoleColor.Blue;
CLRConsole.WriteLine(value);
CLRConsole.ForegroundColor = currentColor;
}
public static System.ConsoleKeyInfo ReadKey(bool intercept)
{
return CLRConsole.ReadKey(intercept);
}
}
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteBlueLine("This text is blue");
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
}
}
}
Nope. Extension method definitions require an instance of the type you're extending. It's unfortunate; I'm not sure why it's required...
You can't add static methods to a type. You can only add (pseudo-)instance methods to an instance of a type.
The point of the this modifier is to tell the C# compiler to pass the instance on the left-side of the . as the first parameter of the static/extension method.
In the case of adding static methods to a type, there is no instance to pass for the first parameter.
As for extension methods, extension methods themselves are static; but they are invoked as if they are instance methods. Since a static class is not instantiable, you would never have an instance of the class to invoke an extension method from. For this reason the compiler does not allow extension methods to be defined for static classes.
Mr. Obnoxious wrote: "As any advanced .NET developer knows, new T() is slow because it generates a call to System.Activator which uses reflection to get the default constructor before calling it".
New() is compiled to the IL "newobj" instruction if the type is known at compile time. Newobj takes a constructor for direct invocation. Calls to System.Activator.CreateInstance() compile to the IL "call" instruction to invoke System.Activator.CreateInstance(). New() when used against generic types will result in a call to System.Activator.CreateInstance(). The post by Mr. Obnoxious was unclear on this point... and well, obnoxious.
This code:
System.Collections.ArrayList _al = new System.Collections.ArrayList();
System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));
produces this IL:
.locals init ([0] class [mscorlib]System.Collections.ArrayList _al,
[1] class [mscorlib]System.Collections.ArrayList _al2)
IL_0001: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
IL_0006: stloc.0
IL_0007: ldtoken [mscorlib]System.Collections.ArrayList
IL_000c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_0011: call object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type)
IL_0016: castclass [mscorlib]System.Collections.ArrayList
IL_001b: stloc.1
I tried to do this with System.Environment back when I was learning extension methods and was not successful. The reason is, as others mention, because extension methods require an instance of the class.
It is not possible to write an extension method, however it is possible to mimic the behaviour you are asking for.
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
}
This will allow you to call Console.WriteBlueLine(fooText) in other classes. If the other classes want access to the other static functions of Console, they will have to be explicitly referenced through their namespace.
You can always add all of the methods in to the replacement class if you want to have all of them in one place.
So you would have something like
using FooConsole = System.Console;
public static class Console
{
public static void WriteBlueLine(string text)
{
FooConsole.ForegroundColor = ConsoleColor.Blue;
FooConsole.WriteLine(text);
FooConsole.ResetColor();
}
public static void WriteLine(string text)
{
FooConsole.WriteLine(text);
}
...etc.
}
This would provide the kind of behaviour you are looking for.
*Note Console will have to be added through the namespace that you put it in.
The following was rejected as an edit to tvanfosson's answer. I was asked to contribute it as my own answer. I used his suggestion and finished the implementation of a ConfigurationManager wrapper. In principle I simply filled out the ... in tvanfosson's answer.
No. Extension methods require an instance of an object. You can
however, write a static wrapper around the ConfigurationManager
interface. If you implement the wrapper, you don't need an extension
method since you can just add the method directly.
public static class ConfigurationManagerWrapper
{
public static NameValueCollection AppSettings
{
get { return ConfigurationManager.AppSettings; }
}
public static ConnectionStringSettingsCollection ConnectionStrings
{
get { return ConfigurationManager.ConnectionStrings; }
}
public static object GetSection(string sectionName)
{
return ConfigurationManager.GetSection(sectionName);
}
public static Configuration OpenExeConfiguration(string exePath)
{
return ConfigurationManager.OpenExeConfiguration(exePath);
}
public static Configuration OpenMachineConfiguration()
{
return ConfigurationManager.OpenMachineConfiguration();
}
public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel)
{
return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
}
public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap)
{
return ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
}
public static void RefreshSection(string sectionName)
{
ConfigurationManager.RefreshSection(sectionName);
}
}
yes, in a limited sense.
public class DataSet : System.Data.DataSet
{
public static void SpecialMethod() { }
}
This works but Console doesn't because it's static.
public static class Console
{
public static void WriteLine(String x)
{ System.Console.WriteLine(x); }
public static void WriteBlueLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.Write(.x);
}
}
This works because as long as it's not on the same namespace. The problem is that you have to write a proxy static method for every method that System.Console have. It's not necessarily a bad thing as you can add something like this:
public static void WriteLine(String x)
{ System.Console.WriteLine(x.Replace("Fck","****")); }
or
public static void WriteLine(String x)
{
System.Console.ForegroundColor = ConsoleColor.Blue;
System.Console.WriteLine(x);
}
The way it works is that you hook something into the standard WriteLine. It could be a line count or bad word filter or whatever. Whenever you just specify Console in your namespace say WebProject1 and import the namespace System, WebProject1.Console will be chosen over System.Console as default for those classes in namespace WebProject1. So this code will turn all the Console.WriteLine calls into blue insofar as you never specified System.Console.WriteLine.
You can use a cast on null to make it work.
public static class YoutTypeExtensionExample
{
public static void Example()
{
((YourType)null).ExtensionMethod();
}
}
The extension:
public static class YourTypeExtension
{
public static void ExtensionMethod(this YourType x) { }
}
YourType:
public class YourType { }
Although the methods of Console are static, its static methods Write() and WriteLine() merely redirect the call to Console.Out.Write() and Console.Out.WriteLine() respectively. Out is an instance whose type derives from the abstract class TextWriter. This makes it possible to define extension methods for TextWriter:
public static class ConsoleTextWriterExtensions
{
public static void WriteBlueLine(this TextWriter writer, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
writer.WriteLine(text);
Console.ResetColor();
}
public static void WriteUppercase(this TextWriter writer, string text)
{
writer.Write(text.ToUpper());
}
}
The method can then be invoked like this:
Console.Out.WriteBlueLine();
And the best part is that the type of the standard error stream instance Console.Error also derives from TextWriter which makes the same extension method also usable for Console.Error:
Console.Error.WriteBlueLine();
This can be quite useful if you have defined an extension method like WriteTable()(for writing a table out to the console) because you can also use it for the error stream or any other object of TextWriter.
Newer versions of C# allow this to be even shorter with a using static statement for Console to get red of the Console. prefix:
using static System.Console;
Out.WriteBlueLine("A blue line");
Error.WriteBlueLine("A blue line");
unfotunately NO, you CANNOT extend static classes
https://onecompiler.com/csharp/3xvbe7axg
using System;
namespace HelloWorld
{
public static class console_extensions {
public static void EXTENSION(this object item) {
System.Console.WriteLine("HELLO THERE!");
}
}
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
Console.EXTENSION();
((Console)null).EXTENSION();
Console l = new Console();
l.EXTENSION();
}
}
}
output
Compilation failed: 4 error(s), 0 warnings
HelloWorld.cs(16,12): error CS0117: `System.Console' does not contain a definition for `EXTENSION'
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
HelloWorld.cs(17,5): error CS0716: Cannot convert to static type `System.Console'
HelloWorld.cs(18,4): error CS0723: `l': cannot declare variables of static types
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
HelloWorld.cs(18,16): error CS0712: Cannot create an instance of the static class `System.Console'
/usr/lib/mono/4.5/mscorlib.dll (Location of the symbol related to previous error)
however you CAN pass null to the extension method
using System;
namespace HelloWorld
{
public static class static_extensions {
public static void print(this object item, int data = 0) {
Console.WriteLine("EXT: I AM A STATIC EXTENSION!");
Console.WriteLine("EXT: MY ITEM IS: " + item);
Console.WriteLine("EXT: MY DATA IS: " + data);
string i;
if (item == null) {
i = "null";
} else {
i = item.GetType().Name;
}
Console.WriteLine("EXT: MY TYPE IS: " + i + "\n");
}
}
public class Program
{
public static void Main(string[] args)
{
// an extension method can be
// called directly
// (null is an instance)
static_extensions.print(null);
// an extension method can also be
// called directly with arguments
// (null is an instance)
static_extensions.print(null, 1);
// an extension method can also be
// called as part of an instance
int x = 0; // initialize int
x.print();
// an extension method can also be
// called as part of an instance
// and with data
int x2 = 0; // initialize int
x2.print(2);
// an extension method can also be
// called directly from null
// since `null` is an instance
((string)null).print();
// an extension method can also be
// called directly from null
// and with data
// since `null` is an instance
((string)null).print(4);
}
}
}
live example: https://onecompiler.com/csharp/3xvbc8s6w
output:
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 0
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 1
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS: 0
EXT: MY DATA IS: 0
EXT: MY TYPE IS: Int32
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS: 0
EXT: MY DATA IS: 2
EXT: MY TYPE IS: Int32
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 0
EXT: MY TYPE IS: null
EXT: I AM A STATIC EXTENSION!
EXT: MY ITEM IS:
EXT: MY DATA IS: 4
EXT: MY TYPE IS: null
I don't really get what people think they'd gain from being able to extend static classes...
What exactly would you be sacrificing by simply doing something like this?
public static class MyConsole
{
public static void WriteBlueLine(string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}
//...
MyConsole.WriteBlueLine("I'm so blue...");
Console.WriteLine("...and I'm not.");
It's minimal extra typing effort and as a bonus, it keeps things transparent...
After all, even a regular extension method is just a shorthand for a helper method. It doesn't allow you to do anything to/with a class (instance) that you wouldn't be able to do from a regular method.
You CAN do this if you are willing to "frig" it a little by making a variable of the static class and assigning it to null. However, this method would not be available to static calls on the class, so not sure how much use it would be:
Console myConsole = null;
myConsole.WriteBlueLine("my blue line");
public static class Helpers {
public static void WriteBlueLine(this Console c, string text)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(text);
Console.ResetColor();
}
}

Why does this code compile without error even though the class is marked Obsoleted?

This is Visual Studio 2008. Obviously has to do with the static class for an extensions.
public class Dummy
{
public readonly int x;
public Dummy(int x)
{
this.x = x;
}
public override string ToString()
{
return x.ToString();
}
}
[Obsolete("Do Not Use", true)]
public static class Extensions
{
public static int Squared(this Dummy Dummy)
{
return Dummy.x * Dummy.x;
}
}
class Program
{
static void Main(string[] args)
{
var d = new Dummy(42);
Console.WriteLine(String.Format("{0}^2={1}", d, d.Squared()));
}
}
That repros in VS2010 as well. Looks like a bug. I'll get it entered in the database.
You can work around the bug by putting the attribute on the actual method.
Thanks for the report!
Calling an obsolete function is a warning, not an error, unless you change the compiler settings to stop on warnings too - to make warnings behave like errors.
Typically I don't see those warnings unless there are other 'real' errors in my code.
Also notice that in your specific case you marked the class as obsolete - not the method. That might matter.
I think You have found a compiler bug:
Although the extension method will be compiled to the static method usage, it seems the compiler does not check it since at the time of compilation it exists in the format of instance method call.
Reason is here. If I declare a second method:
[Obsolete("Do Not Use", true)]
public static class Extensions
{
public static int Squared(this Dummy Dummy)
{
return Dummy.x * Dummy.x;
}
public static int Squared2(Dummy Dummy)
{
return Dummy.x * Dummy.x;
}
}
Now it complains at the 3rd line not second:
class Program
{
static void Main(string[] args)
{
var d = new Dummy(42);
Console.WriteLine(String.Format("{0}^2={1}", d, d.Squared())); // Fine!?
Console.WriteLine(String.Format("{0}^2={1}", d, Extensions.Squared2(d))); // COmplains as expected
}
}
Because it's an extension method, you are not directly accessing the static class, the compiler generates that code.
If, instead, you accessed the Squared method explicitly, you would get the compile-time error.
Extensions.Squared(d)
Since this is an extension method, however, you only implicitly invoke the method, so the attribute will need to be applied to the method itself.
public static class Extensions
{
[Obsolete("Do Not Use", true)]
public static int Squared(this Dummy Dummy)
{
return Dummy.x * Dummy.x;
}
}
On the other hand, deleting (or renaming) the class altogether would serve the same purpose - you will definitely get a compile-time error in that case. :)
EDIT
You can file a bug report with Microsoft here. This seems like behavior that should be handled by the compiler.
I think you need to put the attribute on Squared, not the class.

Categories