Using Explicit Cast in an Extension Method - c#

I have a weird problem when I'm trying to cast an object via an extension method. I have a class where I wrap some functionality around an IPAddress.
// Dumbed down version of class
public sealed class PrefixLengthIPAddress
{
public static explicit operator IPAddress(PrefixLengthIPAddress address)
{
return (address != null) ? address._address : null;
}
public PrefixLengthIPAddress(IPAddress address)
{
_address = address;
_length = address.GetLength();
}
private readonly ushort _length;
private readonly IPAddress _address;
}
I don't like the look of all the parenthesis to extract the IPAddress out of the object:
var family = ((IPAddress)prefixLengthAddress).AddressFamily;
I'd rather be able to do something like this:
var family = prefixLengthAddress.CastAs<IPAddress>().AddressFamily;
In order to do this I wrote the following Extension Method:
public static T CastAs<T>(this object value) where T : class
{
return (T)value;
}
Unfortunately with this I'm getting an InvalidCastException:
var family = ((IPAddress)prefixLengthAddress).AddressFamily; // Works
var family = prefixLengthAddress.CastAs<IPAddress>().AddressFamily; // InvalidCastException
I understand that in this particular case I could simply expose the IPAddress with a getter, but we also have more complex explicit casts that I would like to do this with.
EDIT
Thanks to Chris Sinclair's comment on using dynamic I have updated the extension method to look like:
public static T CastAs<T>(this object value)
{
return (T)((dynamic)value);
}
There is some overhead with using dynamic, but it is more than fast enough for my needs. It also seems to work with all the basic type casting I've tried.

In the first sample you are accessing the user defined conversion. This is only available when the cast operator knows the type of the input is PrefixLengthAddress. In the generic code the compiler only knows the type object and T. There is no way it can access the conversion defined on PrefixLengthAddress in this scenario.
In this scenario what you're doing is much closer to mapping vs. casting since it's actually creating a new value. In terms of LINQ you would want to use Select vs. Cast.

Related

Generic Comparer for all objects with a Text property

I am trying to implement a generic comparer (for a sort) for all objects that have a Text property... so two ASP.net textboxes can be compared, two labels or in this specific case two RadTreeNodes in a telerik RadTreeView (as long as they have a Text property). So I've put the following together to try and so this but get an error as follows:
I have the following code:
public class TextComparer<T> : IComparer
where T : IHasTextProperty
{
public int Compare(object a, object b)
{
T nodeA = (T)a;
T nodeB = (T)b;
return nodeA.Text.CompareTo(nodeB.Text);
}
}
public interface IHasTextProperty
{
string Text { get; set; }
}
Then plan on using it like so...
Array.Sort(nodes, new TextComparer<RadTreeNode>());
but get the following message :
Error 6613 The type 'Telerik.Web.UI.RadTreeNode' cannot be used as
type parameter 'T' in the generic type or method 'TextComparer'.
There is no implicit reference conversion from
'Telerik.Web.UI.RadTreeNode' to 'IHasTextProperty'
I'm sure this is a simple fix, but I'm just a little stumped as to how to fix it.
You are attempting to perform duck-typing in C#, which doesn't support duck-typing. In some languages, you can match a type based on it having a certain property, such as Text in this case. This only works if the language supports this technique.
With C#, a class must explicitly implement an interface for it to be deemed to have that interface's type. Telerik.Web.UI.RadTreeNode doesn't implement IHasTextProperty. T is contrained to types that implement IHasTextProperty, and so you get the error you see.
You really can't use generics in this case. You need to test whether a and b have a Text property. This can be done using reflection or by using dynamic. Neither solution will be as neat as what you were attempting to do unfortunately.
System.Web.UI provides its own IHasTextProperty namely ITextControl (msdn), which behaves exactly like your IHasTextProperty. The downside is that you can not be sure RadTreeNode (or any other 3rd party control) implements this interface.
The only way to be sure is to remove this check from compile time and put it into runtime via reflection, which is quite simple but perhaps not what you want. In case you still want to use it here's an example using an ArgumentException in the TextComparer's constructor to ensure only valid objects are compared.
public class TextComparer<T> : IComparer
{
private bool HasTextProperty(Type t)
{
return (t.GetProperty("Text", typeof(string)) != null);
}
private string GetTextPropertyValue(object obj)
{
return obj.GetType().GetProperty("Text", typeof(string)).GetValue(obj) as string;
}
public TextComparer()
{
if (!HasTextProperty(typeof(T))) throw new ArgumentException(string.Format("{0} doesn't provide a Text property", typeof(T).Name), "T");
}
public int Compare(object x, object y)
{
return GetTextPropertyValue(x).CompareTo(GetTextPropertyValue(y));
}
}

How to Extend the Type Class

this is my code:
bool ch=Type.IsBuiltIn("System.Int32"); // not working-> syntax error
public static class MyExtentions
{
public static bool IsBuiltIn(this Type t, string _type)
{
return (Type.GetType(_type) == null) ? false : true;
}
}
Please I want Extend Type Class by IsBuiltIn new method
You can't have static extension methods. Your extension method works on an instance of the Type class, so to call it you'd have to do something like this:
typeof(Type).IsBuiltIn("System.Int32")
The workaround for this is to just put your extension method in a utility class, e.g. like the following, and call it like a normal static function:
public static class TypeExt
{
public static bool IsBuiltIn(string _type)
{
return Type.GetType(_type) == null;
}
}
// To call it:
TypeExt.IsBuiltIn("System.Int32")
By the way, I don't think this will tell you whether the type is "built-in"; it will merely tell you whether a type with the given name has been loaded into the process.
Extension methods are intended to describe new APIs on instances, not types. In your case, that API would be something like:
Type someType = typeof(string); // for example
bool isBuiltIn = someType.IsBuiltIn("Some.Other.Type");
which... clearly isn't what you wanted; the type here adds nothing and is not related to the IsBuiltIn. There is no compiler trick for adding new static methods to existing types, basically - so you will not be able to use Type.IsBuiltIn("Some.Other.Type").
You can't extend the Type class. You need an instance of the class to create an extension method.
Edit:
See here and here.

Returning desired type from Activator.CreateInstance() instead of object

I'm trying to create an instance of specified Type whatever user wants to have. For a quick illustration of my purpose please see the code below:
static void Main(string[] args)
{
object o = GetInstance(typeof(int));
Console.WriteLine("Created type: {0}", o.GetType().FullName);
}
public static object GetInstance(Type t)
{
Console.WriteLine("Creating instance of {0}", t.FullName);
return Activator.CreateInstance(t);
}
The problem is Activator.CreateInstance() returns object by default. There is also an overload of this method like T Activator.CreateInstance<T>() which is parameterless and returns the type you specify as T.
However, the problem is T should be hard-coded while calling this method and thus should be a fixed value. I am trying to create an instance of desired class and return it as its type.
Right now if you use this method you should write something like:
int i = GetInstance(typeof(int)) as int
I'm trying to reduce this to:
int i = GetInstance(typeof(int))
Is there a way that I can do casting inside the GetInstance and get rid of that as int repetition? By this way, my return type (and also the type I cast the object to) will be unknown at compile time.
Seemed impossible by design to me but I'd really appreciate if you figure it out.
EDIT: Where I'm stuck is e.g. while you're casting, you can do return (T) result if you are in a generic method, but you can't do Type t = ...; return (t) result this doesn't work. You cannot cast to a type which is passed to you as a parameter which is not known at compile time.
Follow a known pattern
This is not a new problem. It is a problem facing any API that allows type-specific return values. For example, a JSON parsing library like Newtonsoft (which is, to wit, the single most popular .NET package downloaded by .NET programmers in 2019) must be able to parse a string and return a type-specific object, which may or may not be known at compile time. It might make sense to follow their example.
Newtonsoft exposes three ways to specify the type when deserializing. You could do as you are currently doing:
//Cast required
var result = JsonConvert.DeserializeObject(text, typeof(MyType)) as MyType;
You can use a generic method:
//No cast required, but you have to hardcode a type as a type parameter
var result = JsonConvert.DeserializeObject<MyType>(text);
Or you can use an instance as a template, which is great for anonymous types, although you can use it with non-anonymous classes as well. This one works via generic type inference:
//No cast required and no need to specify type; the type is inferred from the argument
var result = JsonConvert.DeserializeAnonymousType(text, new MyType());
Here's how you'd do it:
So for you to make this work, your code might look like this:
public object GetInstance(Type type)
{
return Activator.CreateInstance(type);
}
int i = GetInstance(typeof(int)) as int;
public T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
int i = GetInstance<int>();
public T GetInstance<T>(T template)
{
return Activator.CreateInstance<T>();
}
int i = GetInstance(0);
If you do it this way, it's hard to imagine any programmer would have trouble using your library, as the approach should already be familiar to them.
Actually you could write GetInstance like this:
static T GetInstance<T>()
{
return Activator.CreateInstance<T>();
}
And use it:
int j = GetInstance<int>();
This might help you to create instance of desired type:
public class ConcreteFactory<T> : AbstractFactory<T>
{
public override T CreateInstance(string typeName,params object[] parameters)
{
var path = Assembly.GetExecutingAssembly().CodeBase;
var assembly = Assembly.LoadFrom(path);
var type = assembly.GetTypes().SingleOrDefault(t => t.Name == typeName);
return (T)Activator.CreateInstance(type, parameters);
}
}
Key here is generic type T can be used to cast the created instance, this can be used as a template to create instance of any type with parameterized constructor

Generate Generic Type At Runtime

I am wondering if it is possible to use the type of one variable to set as the type of another generic variable?
For example, say I have this code:
public class Foo: IBar<ushort>
{
public FooBar()
{
Value = 0;
}
public ushort Value { get; private set; }
}
I also have this class:
public class FooDTO<TType> : IBar<TType>
{
public TType Value { get; private set; }
}
In these examples, in the interface for IBar has the property
TType Value;
Then in my code I have this
var myFoo = new Foo();
var fooDataType = myFoo.Value.GetType();
//I know this line of code does not compile, but this is what I am looking to be able to do
var myFooDTO= new FooDTO<fooDataType>();
Is what I am looking for possible? Would it be too slow for high use code (because of using reflection.
You can do this via Reflection, by using Type.MakeGenericType.
This will have some overhead due to reflection, so you'd need to profile it to see if that will be an issue for you.
Why not use Method type inference:
public class FooDTO<TType> {
public TType Value { get; private set; }
}
public class Foo : FooDTO<ushort> { }
static FooDTO<T> GetTypedFoo<T>(T Obj) {
return new FooDTO<T>();
}
static void Main(string[] args) {
Foo F = new Foo();
var fooDTO = GetTypedFoo(F.Value);
}
Always when I read "generic" and "runtime" in one sentence, I always thing "bad design" or "doesnt understant what generic means". Possibly both.
Generic parameter is integral part of the type. So saying "Generate Generic Type At Runtime" is same as "Generate Foo class at runtime". You are either looking for reflection or change design of your algorithm.
Also var keyword is not going to help you in this case. Forget about it.
You're looking for compile-time reflection, a feature that C# doesn't have. So if you're looking for performance optimizations, the solutions are worse than the problem.
D does have this feature, though; you can easily write
int x = 0;
typeof(x) y = x + 2;
or even much more complicated expressions in D, and it's all evaluated at compile-time.
The core of what you want is:
var type = typeof(FooDTO<>).MakeGenericType(fooDataType);
object obj = Activator.CreateInstance(type);
however, you'll notice that this is reflection, and pretty much ties you to object. The usual workaround to this is to have access to a non-generic version of the API, so that you can work with object - for example (with the addition of a non-generic IBar):
IBar bar = (IBar)Activator.CreateInstance(type);
You can of course move the runtime/generics hit higher up - perhaps into a generic method; then everything in the generic method can use T, and you can use MakeGenericMethod to execute that method in the context of a particular T known only at runtime.

C# InvalidCastException Though Same Base Class

The following code throws an InvalidCastException.
public static MachineProductCollection MachineProductsForMachine(
MachineProductCollection MachineProductList, int MachineID)
{
return (MachineProductCollection)
MachineProductList.FindAll(c => c.MachineID == MachineID);
}
This surprises me since MachineProductCollection is merely a generic List of MachineProducts which is exactly what FindAll() should return. Here’s the full MachineProductCollection source code. You will note is merely a wrapper for List.
[Serializable]
public partial class MachineProductCollection :
List<MachineProduct>
{
public MachineProductCollection() { }
}
I resorted to the following which basically loops through the FindAll() result which is of type List and adds each item to my MachineProductCollection. Obviously, I don’t like the required iteration.
public static MachineProductCollection
MachineProductForMachine(MachineProductCollection
MachineProductList, int MachineID)
{
MachineProductCollection result =
new MachineProductCollection();
foreach (MachineProduct machineProduct in
MachineProductList.FindAll(c => c.MachineID == MachineID))
{
result.Add(machineProduct);
}
return result;
}
Documentation states an InvalidCastException is thrown when a failure occurs during an explicit reference conversion. Reference conversions are conversions from one reference type to another. While they may change the type of the reference, they never change the type or value of the conversion’s target. Casting objects from one type to another is a frequent cause for this exception.
Considering List is MachineProductCollection’s base, should this really be an InvalidCastException?
Yes, the invalid cast exception is correct. You can freely cast from a derived class to a base class but you cannot blindly cast from a base class to a derived class unless the object really is an instance of the derived class. It's the same reason you cannot do this:
object obj = new object();
string str = (string) obj;
Right? object is string's base, and you cannot freely cast from object to string. On the other hand this would work since obj is indeed a string:
object obj = "foo";
string str = (string) obj;
You're getting the InvalidCastException because a List<MachineProduct> isn't necessarily a MachineProductCollection, even though the reverse is obviously true.
The simplest solution to return an actual MachineProductCollection would be to use List<T>'s sequence constructor:
public static MachineProductCollection
MachineProductForMachine(MachineProductCollection MachineProductList, int MachineID)
{
List<MachineProduct> found =
MachineProductList.FindAll(c => c.MachineID == MachineID))
return new MachineProductCollection(found);
}
However, since you're using lambda expressions I'm guessing you have access to LINQ (via .NET 3.5 or LINQ Bridge), which means you can use the Where extension method to skip the intermediate list:
public static MachineProductCollection
MachineProductForMachine(MachineProductCollection MachineProductList, int MachineID)
{
IEnumerable<MachineProduct> found =
MachineProductList.Where(c => c.MachineID == MachineID))
return new MachineProductCollection(found);
}
That said, you might be better off just returning an IEnumerable<MachineProduct> rather than creating your own collection type. This will probably make your life easier unless you're planning to add special logic to your MachineProductCollection. And in that case, you could always just write extension methods against IEnumerable<MachineProduct> to provide that logic. Just some things to consider...

Categories

Resources