I have a Blazor service which accepts any draggable object, boxed as object. It could be a dragged user, or a classroom, or a scheduled item or almost anything else I might dream up later.
When the draggable is dropped into any component which supports dropping, the component needs to check if it's the right kind of object. For example, the StudentList.razor component will only accept drops if they are IdentityUser or the duple (IdentityUser, string) where the string might be a role name or some other arbitrary info (TBD later):
<div class="class-students-drop" #ondrop="_=>HandleStudentDrop()">
. . .
</div>
#code {
async Task HandleStudentDrop()
{
if (DM.GetItem() is IdentityUser Person)
{
// Do generic user thing (works fine)
}
if (DM.GetItem() is (IdentityUser person,string role) RolePerson)
{
// Do thing based on specified role
// Error (active) CS1061 'object' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'object' could be found
}
}
}
I can pattern-check a class instance like IdentityUser, but I can't figure out how to check if the boxed object fits a particular duple form.
My question: what's the right syntax (if any) to check the signature of a duple using the 'is' keyword?
I've seen examples with pattern-matching duples with values using switch, but I really just want to check if the boxed object is an `(IdentityUser, string) duple.
My references:
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct
Remove the identifier after the tuple type definition:
private object Test()
{
return (new IdentityUser("Me"), "Test");
}
object o = Test();
if(o is (IdentityUser user, string role))
{
Console.WriteLine(user.UserName + " " + role);
}
The quick-and-dirty approach should work:
if( DM.GetItem() is ValueTuple<IdentityUser,String> rolePerson )
{
}
...though you'll lose member names: so use Item1 and Item2 respectively.
Another (better) approach is to use an extension method over Object:
public static class MyExtensions
{
public static Boolean IsTuple<TTuple>( this Object obj, [NotNullWhen(true)] out TTuple? tuple )
where TTuple : struct, ITuple
{
if( obj is TTuple ok )
{
tuple = ok;
return true;
}
else
{
tuple = null;
return false;
}
}
}
Example usage:
void Main()
{
( Int32 x, String y ) tuple = ( 123, "abc" );
Object? obj = tuple;
if( obj.IsTuple( out ( Int32 x, Single y )? notThis ) )
{
notThis.Dump( "Not this" );
}
else if( obj.IsTuple( out ( Int32 x, String y )? thisWorks ) )
{
thisWorks.Dump( "OK" );
}
else
{
"".Dump( "Nor this either." );
}
}
Screenshot proof it works:
In your case, it'd be:
async Task HandleStudentDrop()
{
if (DM.GetItem() is IdentityUser Person)
{
// Do generic user thing (works fine)
}
else if (DM.GetItem().IsTuple( out ( IdentityUser person, String role )? ok ) )
{
<p><b>Name:</b> #( ok.Value.person.Name )</p>
<p><b>Role:</b> #( ok.Value.role )</p>
}
}
Related
I have a set of classes which are defined and populated from parsed XML.
As part of this, I want to be able to dynamically instantiate collection classes for particular types, as specified by the XML (in this case, to manage / instantiate exceptions).
So I have a class, approximately defined as follows:
public class ExceptionGroup<T> : BaseCollectionClass<Exception> where T : Exception
{
// contents of this class don't really matter
}
When I process the XML, I will have the name of the Exception-derived type contained in a string (i.e. "ArgumentNullException", "InvalidDataException", "IndexOutOfRangeException" etc.), as well as the content (message) that will be used to populate the generated Exception (also a string) when/if it's thrown.
So, to get where I'm trying to go, I first have implemented a couple of relevant static classes (defined elsewhere):
// Recursively determines if a supplied Type is ultimately derived from the Exception class:
public static bool IsException( Type type ) =>
(type == typeof( object )) ? false : (type == typeof(Exception)) || IsException( type.BaseType );
// Figures out if the specified string contains the name of a recognized Type
// and that the Type itself is genuinely derived from the Exception base class:
public static Type DeriveType( string name )
{
// If the string is null, empty, whitespace, or malformed, it's not valid and we ignore it...
if ( !string.IsNullOrWhiteSpace( name ) && Regex.IsMatch( name, #"^([a-z][\w]*[a-z0-9])$", RegexOptions.IgnoreCase ) )
try
{
Type excType = System.Type.GetType( name );
if ( IsException( excType ) ) return excType;
}
catch { }
// The type could not be determined, return null:
return null;
}
Using these classes, I can take an input string and end up with a known, existing, C# Type class (presuming the input string is valid) that's derived from the Exception class. Now I want to build a factory method that can create new ExceptionGroup<T> objects where "T" is the object type that's derived from the orignal string.
I've kind of managed this by using the dynamic type as the factory's return type as follows:
public static dynamic CreateExceptionGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( new Type[] { excType } );
return Activator.CreateInstance( groupType );
}
return null;
}
I'm quite uncomfortable with this though, both because I dislike the ambiguity / uncertainty of the result, and because subsequently working with that result can be more cumbersome/complex. I'd much rather actually specify the return type more concretely, and then appropriately cast/qualify it in some manner, for example (yes, I know that this isn't valid!):
public static ExceptionGroup<> CreateGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type[] types = new Type[] { excType };
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
return (ExceptionGroup<>)Activator.CreateInstance( groupType );
}
return null;
}
...but, of course ExceptionGroup<> isn't valid in this syntax/context. (Generates "CS7003: Unexpected use of an unbound generic name"), and neither is merely using ExceptionGroup (Generates: "CS0305: Using the generic type 'ExceptionGroup' requires 1 type arguments.")
So, IS there a way to do this with strong(er) typing, via some other syntax or mechanism, with more precisely described results, or is using dynamic, with all of the subsequent associated overhead, literally the only way to accomplish this?
While I hope that there may be a more simple/ succinct solution (and would love to see it if so!) some retrospective prompting by Sweeper led me to the realisation that I could essentially bypass the problem by injecting the overhead of a new abstract ancestor class:
public abstract class ExceptionGroupFoundation : BaseCollectionClass<Exception>
{
public ExceptionGroupFoundation( object[] args = null ) : base( args ) { }
// Implement necessary common accessors, methods, fields, properties etc here...
// (preferably "abstract" as/when/where possible)
}
... then deriving my generic class from that one:
public class ExceptionGroup<T> : ExceptionGroupFoundation where T : Exception
{
public ExceptionGroup( object[] args = null ) : base( args ) { }
// contents of this class don't really matter
}
... then declaring my factory method using the new abstract class as the return type:
public static ExceptionGroupFoundation CreateGroup( string exceptionTypeName )
{
Type excType = DeriveType( exceptionTypeName );
if ( !(excType is null) )
{
Type[] types = new Type[] { excType };
Type groupType = typeof( ExceptionGroup<> ).MakeGenericType( types );
return (ExceptionGroupFoundation)Activator.CreateInstance( groupType );
}
return null;
}
...to essentially arrive at the desired result, albeit somewhat cumbersomely/awkwardly.
I have a simple case where I want to use pattern matching to identify the algorithm I need to use to perform a collision test between two generic Octrees. My basic case is two Octrees of triangles. The skeleton of the code is.
public class Triangle {
public static bool
Intersects
( IReadOnlyList<Triangle> ta
, IReadOnlyList<Triangle> tb)
{
...
}
}
public class Octree<T> {
public bool Intersects<U>(Octree<U> other)
{
if (this is Octree<Triangle> ota && other is Octree<Triangle> otb)
{
return ota.Intersects( otb, Triangle.Intersects );
}
throw new NotImplementedException();
}
public bool Intersects<U>
( Octree<U> other
, Func<IReadOnlyList<T>, IReadOnlyList<U>, bool> intersectsLeaves
)
{
...
}
}
but results in the below error.
Error CS8121
An expression of type Octree<T> cannot be handled by a pattern of type
Octree<Triangle>.
Of course I can just use typeof(U) and typeof(T) to do the tests but I thought the above should really work. Why doesn't it?
Pattern matching in C# 7.0 has a requirement stating that there must be an explicit or implicit conversion from the left-hand-side type to the right-hand-side type.
In C# 7.1, the spec will be expanded so that either the left-hand-side or the right-hand-side can be an open type.
its a bug. take a look at this:
https://github.com/dotnet/roslyn/issues/16195
There is a work around to the bug/feature. You can use the Try* pattern with inline declared outvariables.
bool TryIs<TU>(object t, out TU u)
{
if (t is TU uu)
{
u = uu;
return true;
}
u = default(TU);
return false;
}
then you can use it like
public bool Intersects<U>(Octree<U> other)
{
if ( TryIs<Octree<Triangle>>(out var ota) && TryIs<Octree<Triangle>>(out var otb))
{
return ota.Intersects( otb, Triangle.Intersects );
}
throw new NotImplementedException();
}
I'm trying to create a delegate of a non-static method on my subclass, ExchangeA. I then want to pass this delegate method to another non-static method on the base-class Exchange. I say non-static because, firstly, they are, and secondly, the examples I have found online have mostly been with regard to static methods.
I have sought this as a resource.
public class Exchange {
public int retrieve( string szLevel, string szJson, Func<string, string, Instrument> delegateMethod) {
// ...
Instrument instrument = delegateMethod(szLevel, szJson)
// ...
return someInt;
}
}
public class ExchangeA : Exchange {
public Instrument instrumentDataProcess( string szLevel, szJson ) {
// ...
return someInstrument;
}
}
public class Instrument { ... }
public class DoStuff {
public static Exchange ExchangeHandler( my args ) {
Exchange oExchange = new ExchangeA(); // Could also be ExchangeB or ExchangeC being instantiated here
Type type = oExchange.GetType(); // type == ExchangeA
System.Reflection.MethodInfo methodInfo = type.GetMethod( "instrumentDataProcess" );
Func<string, string, Instrument> delegateFunc = (Func<string, string, Instrument>)Delegate.CreateDelegate(typeof(Func<string, string, Instrument>), methodInfo); // fails here at runtime
iRet = (int)type.InvokeMember( "retrieve", System.Reflection.BindingFlags.InvokeMethod, null, oExchange, new object[] { szLevel, szCurrencyBase, delegateFunc });
if (iRet == 0)
return nil;
return oExchange;
}
}
It fails during execution when calling the CreateDelegate() method.
The error says:
An exception of type 'System.ArgumentException' occurred in
mscorlib.dll but was not handled in user code
Additional information: Cannot bind to the target method because its
signature or security transparency is not compatible with that of the
delegate type.
Signature or security transparency? I already know the method signature because I defined it and I don't understand what security transparency means... I'm not usually a C# dev. All of my classes and functions are declared as public.
All comments and suggestions are valued! I want to learn the nonsense that is C#! ;)
Update:
Suggestions so far are to pass a method group as a parameter to my retrieve method. This is a new concept for me and I'm not sure how it's done. Does the parameter type Func<string, string, Instrument> still apply?
You don't need a delegate at all, you can just assign the method group:
public class Exchange {
public int retrieve( string szLevel, string szJson, Func<string, string, Instrument> delegateMethod) {
// ...
Instrument instrument = delegateMethod(szLevel, szJson)
// ...
return someInt;
}
}
public class ExchangeA : Exchange {
public Instrument instrumentDataProcess( string szLevel, szJson ) {
// ...
return someInstrument;
}
}
public class Instrument { ... }
public class DoStuff {
public static Exchange ExchangeHandler( my args ) {
Exchange oExchange = new ExchangeA(); // Could also be ExchangeB or ExchangeC being instantiated here
iRet = oExchange.retrieve(szLevel, szCurrencyBase, oExchange.instrumentDataProcess);
if (iRet == 0)
return nil;
return oExchange;
}
}
I have an application containing a certain number of custom TypeConverter. They work well when used in the context of XML parsing.
On another side I am invoking class constructors (based on some previously defined XML tag) using the following (that works so far):
Type MyClass = TypeDelegator.GetType( FullClassName ); // Get the class type
if( MyClass != null ) { // If we find a matching type
ConstructorInfo[] CtorInfos = MyClass.GetConstructors(); // Get constructor infos
if( CtorInfos != null ) { // If we could find a ctor
if( CtorInfos.Length == 1 ) { // We expect 1 Ctor with args
CtorInfos[0].Invoke( new object[] { CtorArg1, CtorArg2 } );
}
}
}
Now I am trying to reuse the TypeConverter from within the invoked constructor. But it does not seems to work. I have been using the regular GetConverter method:
MyType myVar = (MyType)TypeDescriptor.GetConverter( typeof( MyType ) ).ConvertFrom( "1;2;3;4" );
The type class is similar to the following:
public class MyType {
private int PrivateField;
public MyType() { }
public MyType( String Arg1, Char Arg2 ) { }
public int Prop1 { get { return ( 4 ); } }
public int Prop2 {
get { return ( PrivateField ); }
set { PrivateField = value; }
}
}
Digging into the problem it seems that TypeDescriptor.GetConverter does not find the custom type converter (that are part of the same assembly).
var debugVar = TypeDescriptor.GetConverter( typeof( MyType ) );
always return a TypeConverter as opposed to an expected "TypeConverterMyType". That make me think "TypeConverterMyType" is not accessible probably because of the invoke.
[Update]
It seems that the TypeDescriptor.GetConverter works for enum types but not for class based type. I think I am passing next to an important part of the puzzle... :?
So the question :
1- Is it actually possible to access a custom type converter from an invoked method?
2- Is there a better/smarter way to handle this?
3- I am using .Net 3.5, should I expect the same behaviour if I upgrade to 4.0 or 4.5?
I have two classes.
public class Handler
{
delegate T ReturnDelegate();
public T HandleReturnMethod(ReturnDelegate d)
{
DoSomething(); //some other stuff
return d();
}
}
public partial class Test
{
protected int Id
{
get
{
return new Handler().HandleReturnMethod(delegate()
{
int id = 1;
return id;
});
}
}
}
I want to be able to handle any Type in the Handler class. But 'T' seems not to work, int cannot be converted to it.
If there is a better approach or something is wrong here, please comment.
You need to create the delegate type and method to use generics. Personally, I would use a standard delegate for this:
public T HandleReturnMethod<T>(Func<T> d)
{
DoSomething(); //some other stuff
return d();
}
Given the above, your code should work:
protected int Id
{
get
{
return new Handler().HandleReturnMethod(() =>
{
int id = 1;
return id;
});
}
}
The reason your code wasn't working is because int ( Int32 ) is a structure not an object which is the reason it cannot normally be nullable
unless you declare it as int? num = null;
If I am not mistaken only an object can have a Type ( both an object and a Type are classes ) not a structure. All classes are based on the object class ( which is the reason a collection that isn't a generic ( i.e. Type t ) elements are objects.
http://msdn.microsoft.com/en-us/library/1t3y8s4s(v=VS.100).aspx
http://msdn.microsoft.com/en-us/library/ah19swz4.aspx
http://msdn.microsoft.com/en-us/library/ah19swz4(v=VS.100).aspx
http://msdn.microsoft.com/en-us/library/system.int32.aspx
http://msdn.microsoft.com/en-us/library/system.type.aspx
http://msdn.microsoft.com/en-us/library/system.object.aspx