.NET vb6 wrapper property with parameter - c#

i need to write a c# wrapper for a vb6 application. I always get error 450 ( Wrong number of arguments or property assignment was not valid.) This is my VB Code
Dim DBEngine As New DBEngineNet
Set mDbEProp = DBEngine.Properties("Version") ' <-- ERROR
This code is working, so the problem is the parameter of the property
Dim DBEngine As New DBEngineNet
Set mDbEProps = DBEngine.Properties
Set mDbEProp = mDbEProps("Version") '<-- Working. Results 1.0
Here is my COM-Visible C#-Code. It uses the Interop-Interfaces of the old VB6-MotorApp.
[ComVisible(true)]
public class DBEngineNet : VB6MotorApp.DBEngine
{
public VB6MotorApp.Properties Properties
{
// [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_SAFEARRAY)] Maybe something like this???
get
{
return new PropertiesNet
{
new PropertyNet{Name="Version", Value="1.0"}
};
}
}
Here is the Properties-Object:
[ComVisible(true)]
public class PropertiesNet : VB6MotorApp.Properties, IList<PropertyNet>
{
List<PropertyNet> _properties = new List<PropertyNet>();
public VB6MotorApp.Property this[object Item]
{
get
{
return _properties.FirstOrDefault(p => p.Name == Item.ToString());
}
}
}
Any ideas?

The basic diagnostic tool you need here is OleView.exe, run it from the Visual Studio Command Prompt. Use its File + View typelib command to look at the type libraries and compare them. First on your original VB6 implementation so you have a base-line, next on the type library for your .NET version.
There are inevitably going to be major difference the way you are doing it now, you are exposing too many details of the class implementation. All of the System.Object methods as well as the IList<> implementation methods are going to be visible. Boilerplate is to declare a [ComVisible(true)] interface (VB6 likes their name to start with an _underscore) and hide the class implementation by giving it the [ClassInterface(ClassInterfaceType.None)] attribute. You already have the interface so only the attribute should be necessary.
What you want to look for first in the OleView.exe output is the [dispid] attribute for the DBEngineNet.Properties property. It doesn't act like the default property which is why you have to obtain the property value explicitly in your VB6 code. The default property has dispid(0). You force the value in .NET code by giving it the [DispId(0)] attribute.
You also want to look at the original type library, "VB6MotorApp.Properties" looks wrong. That's a coclass name, not an interface name. Non-zero odds that you should be using VB6MotorApp._Properties. Same for VB6MotorApp._DBEngine.
And look at which interfaces in the coclasses have the [default] attribute. It should be the VB6 interfaces. Probably not an issue if your VB6 snippets work as posted.

Related

c# Can I prevent my constructor arguments clashing with VBA instantiation?

I'm a bit confused about how I can override constructors requiring arguments when using their classes in a VBA environment.
What works?
I've created several classes in a library, each with an interface to allow full intellisense compatibility when using this library in VBA
With or without constructors, these classes work fine for me e.g.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("JamHeadArt.ClassEX")]
[Guid("XYZ")]
public partial class ClassEX : IClassEX
{
public ClassEX()
{
// Empty constructor here, some of mine have processes, all work well
}
// Methods/ Properties as outlined by the interface below
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("ABC")]
public interface IClassEx
{
// Various methods / fields / properties to be implemented by ClassEX
}
I then add reference to my library and write simple lines of code in VBA to instantiate and access my classes:
Sub Test()
Dim t As JamHeadArt.ClassEX
Set t = New JamHeadArt.ClassEX
' Using t.dot then provides all the methods needed '
End Sub
What goes wrong?
When I create contstructors with arguments (even if optional) in a class, VBA will stop allowing me to create instances of these classes, it tells me the "New" keyword is Invalid and actually won't allow me to choose the class from the intellisense list of objects in my library if I go straight for Dim t As New JamHeadArt.ClassEx even if the parameters are set to optional (therefore not really needed)
The annoying thing here is - I don't actually want my VBA instances to accept parameters via the constructor, they're mainly there for Unit testing and they're optional strings so default to "" ... so I guess my question is something like
Is it possible to override any constructor parameters so when referenced in a VBA environment it will ignore them?
e.g. I really want my constructor to look like this:
public ClassEX(string s = "")
{
}
and in VBA it should work as before Dim t As New JamHeadArt.ClassEX - but it won't with that optional string in there!
You can add an additional constructor, for example:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("JamHeadArt.ClassEX")]
[Guid("XYZ")]
public partial class ClassEX : IClassEX
{
public ClassEX()
{
// Empty constructor here, some of mine have processes, all work well
}
public ClassEX(string foo)
{
// additional constructor, can be used for unit testing etc.
}
// Methods/ Properties as outlined by the interface below
}

expose c++/CLI templated wrapper to c#

I am currently working on a visual editor to build Finite State Machines. The core is c++ since the built FSM will run in a game. The editor is c#. I was able to get a CLI wrapper going so I can build everything I need in the c# side.
The last thing I am trying to do, is to be able to expose a templated class to c#.
I started by creating a managed class:
template <typename T>
public ref class TestTemp
{
private:
ClassToWrap<T>* m_val;
public:
TestTemp(T val) :
{
m_val = new ClassToWrap<T>();
}
}
Then since templates are generated at compile time, I am forcing the generation of the type with template specialization.
template ref class FSMWrapper::TestTemp<float>;
I tried to to the specialization in several places, cpp, header, also in main just in case, I even tried specific instantiation in main like this:
FSMWrapper::TestTemp<float> t(10.0f);
I even tried to explicitely tell to export the symbol like I would do in regular c++ but compiler complains that I cannot do that with a managed type.
After all this I did not managed to get the symbol appear in the c# namespace, (everything else appears, so yeah, the wrapper works as expected).
Also If I remove the template in the wrapper and just call it TestTempFloat and force internally an instantiation of float it works.
public ref class TestTempFloat
{
private:
ClassToWrap<float> m_val;
public:
TestTempFloat(float val) :
{
m_val = new ClassToWrap<float>();
}
};
What I am trying to do, is it even possible? By googling around looks like it is, but people just say, wrap it in a CLI type and force the symbol generation.
If is possible what am I doing wrong?
If is not possible I will just have to do the specialized wraps manually, not pretty but I know it works.
I also tried to wrap it in a generic instead of a template, but then I cannot feed the T generic type as a template type.
PS: I know there is not a destructor to free the memory, this is just a dummy test to keep example short.
As you have seen, managed templates aren't accessible outside of the assembly where they are located. Basically the idea is to expose the managed template as a generic interface which can be sent across assembly boundaries.
So in your case you would want to create a ITestTemp, something like this...
generic<typename T> public interface class ITestTemp
{
public:
TestTemp(T val);
}
That is the interface that you will export across assemblies. Now you will have to convert your managed template into that generic interface to do so you can use inheritance, something with the following signature (internals omitted for simplicity)
templace<typename T> ref class TestTemp : ITestTemp<T>
Once you have that now you will have to do the "compiler's work" (what is normally just automagically handled for a regular C++ template) for it to convert between the two so to speak. So you will have to create a factory method that will create the specific instances that you are looking for. It would look like this
public ref class TestTempFactory
{
public:
generic<typename T> static ITestTemp<T>^ Create()
{
if (T::typeid == String::typeid)
{ return (ITestTemp<T>^) gcnew TestTemp<String>(); }
//more cases as needed...
}
}
I hope that explains it well enough, if not let me know.
If you implement a full-fledged subclass of your template class, that should do the trick. You will need to implement all the constructors, but just as a pass-through to the base class's constructors; no actual code.
public ref class TestTempFloat : TestTemp<float>
{
TestTempFloat(float val) : TestTemp(val) { };
};
If you have a lot of these, you could make use of the preprocessor:
#define IMPLEMENT_TESTTEMP(namesuffix, type) \
public ref class TestTemp ## namesuffix : TestTemp<type> \
{ \
TestTemp ## namesuffix(type val) : TestTemp(val) { }; \
};
IMPLEMENT_TESTTEMP(Float, float)
IMPLEMENT_TESTTEMP(Double, double)
IMPLEMENT_TESTTEMP(Int, int)

How the meta tags of a method work? [duplicate]

What are attributes in .NET, what are they good for, and how do I create my own attributes?
Metadata. Data about your objects/methods/properties.
For example I might declare an Attribute called: DisplayOrder so I can easily control in what order properties should appear in the UI. I could then append it to a class and write some GUI components that extract the attributes and order the UI elements appropriately.
public class DisplayWrapper
{
private UnderlyingClass underlyingObject;
public DisplayWrapper(UnderlyingClass u)
{
underlyingObject = u;
}
[DisplayOrder(1)]
public int SomeInt
{
get
{
return underlyingObject .SomeInt;
}
}
[DisplayOrder(2)]
public DateTime SomeDate
{
get
{
return underlyingObject .SomeDate;
}
}
}
Thereby ensuring that SomeInt is always displayed before SomeDate when working with my custom GUI components.
However, you'll see them most commonly used outside of the direct coding environment. For example the Windows Designer uses them extensively so it knows how to deal with custom made objects. Using the BrowsableAttribute like so:
[Browsable(false)]
public SomeCustomType DontShowThisInTheDesigner
{
get{/*do something*/}
}
Tells the designer not to list this in the available properties in the Properties window at design time for example.
You could also use them for code-generation, pre-compile operations (such as Post-Sharp) or run-time operations such as Reflection.Emit.
For example, you could write a bit of code for profiling that transparently wrapped every single call your code makes and times it. You could "opt-out" of the timing via an attribute that you place on particular methods.
public void SomeProfilingMethod(MethodInfo targetMethod, object target, params object[] args)
{
bool time = true;
foreach (Attribute a in target.GetCustomAttributes())
{
if (a.GetType() is NoTimingAttribute)
{
time = false;
break;
}
}
if (time)
{
StopWatch stopWatch = new StopWatch();
stopWatch.Start();
targetMethod.Invoke(target, args);
stopWatch.Stop();
HandleTimingOutput(targetMethod, stopWatch.Duration);
}
else
{
targetMethod.Invoke(target, args);
}
}
Declaring them is easy, just make a class that inherits from Attribute.
public class DisplayOrderAttribute : Attribute
{
private int order;
public DisplayOrderAttribute(int order)
{
this.order = order;
}
public int Order
{
get { return order; }
}
}
And remember that when you use the attribute you can omit the suffix "attribute" the compiler will add that for you.
NOTE: Attributes don't do anything by themselves - there needs to be some other code that uses them. Sometimes that code has been written for you but sometimes you have to write it yourself. For example, the C# compiler cares about some and certain frameworks frameworks use some (e.g. NUnit looks for [TestFixture] on a class and [Test] on a test method when loading an assembly).
So when creating your own custom attribute be aware that it will not impact the behaviour of your code at all. You'll need to write the other part that checks attributes (via reflection) and act on them.
Many people have answered but no one has mentioned this so far...
Attributes are used heavily with reflection. Reflection is already pretty slow.
It is very worthwhile marking your custom attributes as being sealed classes to improve their runtime performance.
It is also a good idea to consider where it would be appropriate to use place such an attribute, and to attribute your attribute (!) to indicate this via AttributeUsage. The list of available attribute usages might surprise you:
Assembly
Module
Class
Struct
Enum
Constructor
Method
Property
Field
Event
Interface
Parameter
Delegate
ReturnValue
GenericParameter
All
It's also cool that the AttributeUsage attribute is part of the AttributeUsage attribute's signature. Whoa for circular dependencies!
[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
Attributes are a kind of meta data for tagging classes. This is often used in WinForms for example to hide controls from the toolbar, but can be implemented in your own application to enable instances of different classes to behave in specific ways.
Start by creating an attribute:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class SortOrderAttribute : Attribute
{
public int SortOrder { get; set; }
public SortOrderAttribute(int sortOrder)
{
this.SortOrder = sortOrder;
}
}
All attribute classes must have the suffix "Attribute" to be valid.
After this is done, create a class that uses the attribute.
[SortOrder(23)]
public class MyClass
{
public MyClass()
{
}
}
Now you can check a specific class' SortOrderAttribute (if it has one) by doing the following:
public class MyInvestigatorClass
{
public void InvestigateTheAttribute()
{
// Get the type object for the class that is using
// the attribute.
Type type = typeof(MyClass);
// Get all custom attributes for the type.
object[] attributes = type.GetCustomAttributes(
typeof(SortOrderAttribute), true);
// Now let's make sure that we got at least one attribute.
if (attributes != null && attributes.Length > 0)
{
// Get the first attribute in the list of custom attributes
// that is of the type "SortOrderAttribute". This should only
// be one since we said "AllowMultiple=false".
SortOrderAttribute attribute =
attributes[0] as SortOrderAttribute;
// Now we can get the sort order for the class "MyClass".
int sortOrder = attribute.SortOrder;
}
}
}
If you want to read more about this you can always check out MSDN which has a pretty good description.
I hope this helped you out!
An attribute is a class that contains some bit of functionality that you can apply to objects in your code. To create one, create a class that inherits from System.Attribute.
As for what they're good for... there are almost limitless uses for them.
http://www.codeproject.com/KB/cs/dotnetattributes.aspx
Attributes are like metadata applied to classes, methods or assemblies.
They are good for any number of things (debugger visualization, marking things as obsolete, marking things as serializable, the list is endless).
Creating your own custom ones is easy as pie. Start here:
http://msdn.microsoft.com/en-us/library/sw480ze8(VS.71).aspx
In the project I'm currently working on, there is a set of UI objects of various flavours and an editor to assembly these objects to create pages for use in the main application, a bit like the form designer in DevStudio. These objects exist in their own assembly and each object is a class derived from UserControl and has a custom attribute. This attribute is defined like this:
[AttributeUsage (AttributeTargets::Class)]
public ref class ControlDescriptionAttribute : Attribute
{
public:
ControlDescriptionAttribute (String ^name, String ^description) :
_name (name),
_description (description)
{
}
property String ^Name
{
String ^get () { return _name; }
}
property String ^Description
{
String ^get () { return _description; }
}
private:
String
^ _name,
^ _description;
};
and I apply it to a class like this:
[ControlDescription ("Pie Chart", "Displays a pie chart")]
public ref class PieControl sealed : UserControl
{
// stuff
};
which is what the previous posters have said.
To use the attribute, the editor has a Generic::List <Type> containing the control types. There is a list box which the user can drag from and drop onto the page to create an instance of the control. To populate the list box, I get the ControlDescriptionAttribute for the control and fill out an entry in the list:
// done for each control type
array <Object ^>
// get all the custom attributes
^attributes = controltype->GetCustomAttributes (true);
Type
// this is the one we're interested in
^attributetype = ECMMainPageDisplay::ControlDescriptionAttribute::typeid;
// iterate over the custom attributes
for each (Object ^attribute in attributes)
{
if (attributetype->IsInstanceOfType (attribute))
{
ECMMainPageDisplay::ControlDescriptionAttribute
^description = safe_cast <ECMMainPageDisplay::ControlDescriptionAttribute ^> (attribute);
// get the name and description and create an entry in the list
ListViewItem
^item = gcnew ListViewItem (description->Name);
item->Tag = controltype->Name;
item->SubItems->Add (description->Description);
mcontrols->Items->Add (item);
break;
}
}
Note: the above is C++/CLI but it's not difficult to convert to C#
(yeah, I know, C++/CLI is an abomination but it's what I have to work with :-( )
You can put attributes on most things and there are whole range of predefined attributes. The editor mentioned above also looks for custom attributes on properties that describe the property and how to edit it.
Once you get the whole idea, you'll wonder how you ever lived without them.
As said, Attributes are relatively easy to create. The other part of the work is creating code that uses it. In most cases you will use reflection at runtime to alter behavior based on the presence of an attribute or its properties. There are also scenarios where you will inspect attributes on compiled code to do some sort of static analysis. For example, parameters might be marked as non-null and the analysis tool can use this as a hint.
Using the attributes and knowing the appropriate scenarios for their use is the bulk of the work.
Attributes are, essentially, bits of data you want to attach to your types (classes, methods, events, enums, etc.)
The idea is that at run time some other type/framework/tool will query your type for the information in the attribute and act upon it.
So, for example, Visual Studio can query the attributes on a 3rd party control to figure out which properties of the control should appear in the Properties pane at design time.
Attributes can also be used in Aspect Oriented Programming to inject/manipulate objects at run time based on the attributes that decorate them and add validation, logging, etc. to the objects without affecting the business logic of the object.
You can use custom attributes as a simple way to define tag values in sub classes without having to write the same code over and over again for each subclass. I came across a nice concise example by John Waters of how to define and use custom attributes in your own code.
There is a tutorial at http://msdn.microsoft.com/en-us/library/aa288454(VS.71).aspx
To get started creating an attribute, open a C# source file, type attribute and hit [TAB]. It will expand to a template for a new attribute.
Attributes are also commonly used for Aspect Oriented Programming. For an example of this check out the PostSharp project.

.NET resolves a class to the wrong DLL

My C# project has references to two third party DLLs. This is important because it means I don't have access to source code and can't modify or recompile these two DLLs.
Let's call them dll A and dll B. Here's what dll A looks like:
namespace ThirdParty.Foo
{
public class Bar
{
...snip...
}
public class Something
{
public Bar MyProperty { get; set; }
}
}
And here's what dll B looks like:
namespace ThirdParty.Foo
{
public class Bar
{
...snip...
}
public class SomethingElse
{
public Bar MyProperty { get; set; }
}
}
As you can see, they have the same namespace and they both define a class with the same name. My C# code needs a reference to both DLLs. I use the alias property on the reference to be able to distinguish between the two references and I also extern alias firstDll and extern alias secondDll at the top of my C# file. So far so good.
It seems obvious to me that the type of Something.MyProperty is firstDll.ThirdParty.Foo.Bar and the type of SomethingElse.MyProperty is secondDll.ThirdParty.Foo.Bar but for some reason, Visual Studio gets confused and resolves the type of both properties to the same Bar class in firstDll.
Is there a way for me to "force" VisualStudio to resolve the correct type?
EDIT:
the error I'm getting in Visual Studio is: Cannot implicitly convert type 'ThirdParty.Foo.Bar [d:\MySolution\References\Second.dll]' to 'ThirpParty.Foo.Bar [d:\MySolution\References\First.dll]'
Create a DLL that will serve as a wrapper for DLL A. Let's call the new DLL "C". Your project will then reference DLL B and C.
If the two types have the same name and namespace, then you are pretty much stuck. In C#, the name/identifier is quite important, "best fit" will only work in special situations.
However, you could write wrappers for the two different types, and make those wrappers have different names. By having two different (or even one) project just for the wrapper, you could have only one reference, thus effectively resolving the conflict "by force".
Something that works(for me it did referenced correctly) is the following,first(as you probably already did)click each dll in the references and assign an alias and place the proper extern alias.After to use the class Something and SomethingElse(and properly assign the Bar Property) create one class for each of those classes(Something and SomethingElse)and derive from them and shadow the MyProperty property:
public class TestFirst : first.ThirdParty.Foo.Something
{
//here you shadow and since you must provide the alias
//and the fully qualified name it will bet set to the
//right Bar class,same bellow in testsecond.
public first.ThirdParty.Foo.Bar MyProperty { get; set; }
}
public class TestSecond : second.ThirdParty.Foo.SomethingElse
{
public second.ThirdParty.Foo.Bar MyProperty { get; set; }
}
After its just business as usual:
TestSecond t = new TestSecond();
t.MyProperty = new second.ThirdParty.Foo.Bar();
I would load the dlls explicitly via Assembly.Load then do a createinstance on the types you need then invoke the methods via dynamic - that's because I am lazy.
The other (not lazy) way is to then use reflection to find the method and invoke it.
Since you have an extreme case here where the two assemblies not only have the same namespace, but also the same full assembly name (meaning the types have the same assembly-qualified name), you may want to consider more extreme measures to resolve it. If it's a fully managed DLL (no native code mixed in) and not strong named (or you're okay with removing the strong naming), you may be able to get away with a procedure like this:
Run ildasm and output to a .il file on disk.
Modify the .il file using a careful search and replace to change the root namespace for all types (be cautious for inner namespaces or type names that contain the same text as the root namespace.)
Run the modified file through ilasm to build a new .dll with the changes.
You can use the keyword var instead of specifying the type explicitly to avoid ambiguous references
var x = new ThirdParty.Foo.Something();
var y = new ThirdParty.Foo.SomethingElse();
var barX = x.MyProperty;
var barY = y.MyProperty;

What are attributes in .NET?

What are attributes in .NET, what are they good for, and how do I create my own attributes?
Metadata. Data about your objects/methods/properties.
For example I might declare an Attribute called: DisplayOrder so I can easily control in what order properties should appear in the UI. I could then append it to a class and write some GUI components that extract the attributes and order the UI elements appropriately.
public class DisplayWrapper
{
private UnderlyingClass underlyingObject;
public DisplayWrapper(UnderlyingClass u)
{
underlyingObject = u;
}
[DisplayOrder(1)]
public int SomeInt
{
get
{
return underlyingObject .SomeInt;
}
}
[DisplayOrder(2)]
public DateTime SomeDate
{
get
{
return underlyingObject .SomeDate;
}
}
}
Thereby ensuring that SomeInt is always displayed before SomeDate when working with my custom GUI components.
However, you'll see them most commonly used outside of the direct coding environment. For example the Windows Designer uses them extensively so it knows how to deal with custom made objects. Using the BrowsableAttribute like so:
[Browsable(false)]
public SomeCustomType DontShowThisInTheDesigner
{
get{/*do something*/}
}
Tells the designer not to list this in the available properties in the Properties window at design time for example.
You could also use them for code-generation, pre-compile operations (such as Post-Sharp) or run-time operations such as Reflection.Emit.
For example, you could write a bit of code for profiling that transparently wrapped every single call your code makes and times it. You could "opt-out" of the timing via an attribute that you place on particular methods.
public void SomeProfilingMethod(MethodInfo targetMethod, object target, params object[] args)
{
bool time = true;
foreach (Attribute a in target.GetCustomAttributes())
{
if (a.GetType() is NoTimingAttribute)
{
time = false;
break;
}
}
if (time)
{
StopWatch stopWatch = new StopWatch();
stopWatch.Start();
targetMethod.Invoke(target, args);
stopWatch.Stop();
HandleTimingOutput(targetMethod, stopWatch.Duration);
}
else
{
targetMethod.Invoke(target, args);
}
}
Declaring them is easy, just make a class that inherits from Attribute.
public class DisplayOrderAttribute : Attribute
{
private int order;
public DisplayOrderAttribute(int order)
{
this.order = order;
}
public int Order
{
get { return order; }
}
}
And remember that when you use the attribute you can omit the suffix "attribute" the compiler will add that for you.
NOTE: Attributes don't do anything by themselves - there needs to be some other code that uses them. Sometimes that code has been written for you but sometimes you have to write it yourself. For example, the C# compiler cares about some and certain frameworks frameworks use some (e.g. NUnit looks for [TestFixture] on a class and [Test] on a test method when loading an assembly).
So when creating your own custom attribute be aware that it will not impact the behaviour of your code at all. You'll need to write the other part that checks attributes (via reflection) and act on them.
Many people have answered but no one has mentioned this so far...
Attributes are used heavily with reflection. Reflection is already pretty slow.
It is very worthwhile marking your custom attributes as being sealed classes to improve their runtime performance.
It is also a good idea to consider where it would be appropriate to use place such an attribute, and to attribute your attribute (!) to indicate this via AttributeUsage. The list of available attribute usages might surprise you:
Assembly
Module
Class
Struct
Enum
Constructor
Method
Property
Field
Event
Interface
Parameter
Delegate
ReturnValue
GenericParameter
All
It's also cool that the AttributeUsage attribute is part of the AttributeUsage attribute's signature. Whoa for circular dependencies!
[AttributeUsageAttribute(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
Attributes are a kind of meta data for tagging classes. This is often used in WinForms for example to hide controls from the toolbar, but can be implemented in your own application to enable instances of different classes to behave in specific ways.
Start by creating an attribute:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class SortOrderAttribute : Attribute
{
public int SortOrder { get; set; }
public SortOrderAttribute(int sortOrder)
{
this.SortOrder = sortOrder;
}
}
All attribute classes must have the suffix "Attribute" to be valid.
After this is done, create a class that uses the attribute.
[SortOrder(23)]
public class MyClass
{
public MyClass()
{
}
}
Now you can check a specific class' SortOrderAttribute (if it has one) by doing the following:
public class MyInvestigatorClass
{
public void InvestigateTheAttribute()
{
// Get the type object for the class that is using
// the attribute.
Type type = typeof(MyClass);
// Get all custom attributes for the type.
object[] attributes = type.GetCustomAttributes(
typeof(SortOrderAttribute), true);
// Now let's make sure that we got at least one attribute.
if (attributes != null && attributes.Length > 0)
{
// Get the first attribute in the list of custom attributes
// that is of the type "SortOrderAttribute". This should only
// be one since we said "AllowMultiple=false".
SortOrderAttribute attribute =
attributes[0] as SortOrderAttribute;
// Now we can get the sort order for the class "MyClass".
int sortOrder = attribute.SortOrder;
}
}
}
If you want to read more about this you can always check out MSDN which has a pretty good description.
I hope this helped you out!
An attribute is a class that contains some bit of functionality that you can apply to objects in your code. To create one, create a class that inherits from System.Attribute.
As for what they're good for... there are almost limitless uses for them.
http://www.codeproject.com/KB/cs/dotnetattributes.aspx
Attributes are like metadata applied to classes, methods or assemblies.
They are good for any number of things (debugger visualization, marking things as obsolete, marking things as serializable, the list is endless).
Creating your own custom ones is easy as pie. Start here:
http://msdn.microsoft.com/en-us/library/sw480ze8(VS.71).aspx
In the project I'm currently working on, there is a set of UI objects of various flavours and an editor to assembly these objects to create pages for use in the main application, a bit like the form designer in DevStudio. These objects exist in their own assembly and each object is a class derived from UserControl and has a custom attribute. This attribute is defined like this:
[AttributeUsage (AttributeTargets::Class)]
public ref class ControlDescriptionAttribute : Attribute
{
public:
ControlDescriptionAttribute (String ^name, String ^description) :
_name (name),
_description (description)
{
}
property String ^Name
{
String ^get () { return _name; }
}
property String ^Description
{
String ^get () { return _description; }
}
private:
String
^ _name,
^ _description;
};
and I apply it to a class like this:
[ControlDescription ("Pie Chart", "Displays a pie chart")]
public ref class PieControl sealed : UserControl
{
// stuff
};
which is what the previous posters have said.
To use the attribute, the editor has a Generic::List <Type> containing the control types. There is a list box which the user can drag from and drop onto the page to create an instance of the control. To populate the list box, I get the ControlDescriptionAttribute for the control and fill out an entry in the list:
// done for each control type
array <Object ^>
// get all the custom attributes
^attributes = controltype->GetCustomAttributes (true);
Type
// this is the one we're interested in
^attributetype = ECMMainPageDisplay::ControlDescriptionAttribute::typeid;
// iterate over the custom attributes
for each (Object ^attribute in attributes)
{
if (attributetype->IsInstanceOfType (attribute))
{
ECMMainPageDisplay::ControlDescriptionAttribute
^description = safe_cast <ECMMainPageDisplay::ControlDescriptionAttribute ^> (attribute);
// get the name and description and create an entry in the list
ListViewItem
^item = gcnew ListViewItem (description->Name);
item->Tag = controltype->Name;
item->SubItems->Add (description->Description);
mcontrols->Items->Add (item);
break;
}
}
Note: the above is C++/CLI but it's not difficult to convert to C#
(yeah, I know, C++/CLI is an abomination but it's what I have to work with :-( )
You can put attributes on most things and there are whole range of predefined attributes. The editor mentioned above also looks for custom attributes on properties that describe the property and how to edit it.
Once you get the whole idea, you'll wonder how you ever lived without them.
As said, Attributes are relatively easy to create. The other part of the work is creating code that uses it. In most cases you will use reflection at runtime to alter behavior based on the presence of an attribute or its properties. There are also scenarios where you will inspect attributes on compiled code to do some sort of static analysis. For example, parameters might be marked as non-null and the analysis tool can use this as a hint.
Using the attributes and knowing the appropriate scenarios for their use is the bulk of the work.
Attributes are, essentially, bits of data you want to attach to your types (classes, methods, events, enums, etc.)
The idea is that at run time some other type/framework/tool will query your type for the information in the attribute and act upon it.
So, for example, Visual Studio can query the attributes on a 3rd party control to figure out which properties of the control should appear in the Properties pane at design time.
Attributes can also be used in Aspect Oriented Programming to inject/manipulate objects at run time based on the attributes that decorate them and add validation, logging, etc. to the objects without affecting the business logic of the object.
You can use custom attributes as a simple way to define tag values in sub classes without having to write the same code over and over again for each subclass. I came across a nice concise example by John Waters of how to define and use custom attributes in your own code.
There is a tutorial at http://msdn.microsoft.com/en-us/library/aa288454(VS.71).aspx
To get started creating an attribute, open a C# source file, type attribute and hit [TAB]. It will expand to a template for a new attribute.
Attributes are also commonly used for Aspect Oriented Programming. For an example of this check out the PostSharp project.

Categories

Resources