I have the following class:
public class HandleResourceReferencesParams
{
public Factory Factory { get; set; }
public DataObject Resource { get; set; }
public HandleAction Action { get; set; }
public enum HandleAction
{
Activate,
Disable,
Terminate
}
}
Which is used in the following code:
var parameters = new HandleResourceReferencesParams();
parameters.Factory = context.Factory;
parameters.Resource = resource;
parameters.Action = parameters.HandleAction.Terminate; // Does not compile
HandleResourceReferences(parameters);
By using parameters.HandleAction, I get a compile error:
Cannot access static enum 'HandleAction' in non-static context
The enum is clearly not declared 'static'. Why does it have a static context when it is referenced from an object instance (non static as well)?
EDIT:
I already found the solution mentioned by Tim (Thanks by the way). I am just trying to understand why I am getting this error.
The error message is unfortunate, but it's not unfortunate that you can't do it... you're trying to access a member of a type, rather than a member of an instance of the type, but you're doing so "via" an instance.
Basically, it's the same reason that this code fails to compile:
Thread t = new Thread(...);
t.Start();
t.Sleep(1000); // Nope, Sleep is a static method
All nested types are effectively static members, in that you can't have a type which is specific to an instance of the containing type.
From the C# spec, section 10.3.7 (emphasis mine):
When a field, method, property, event, operator or constructor declaration includes a static modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member.
So the enum is a static member of the type, despite not having the static modifier.
Use
parameters.Action = HandleResourceReferencesParams.HandleAction.Terminate;
You assign an enum to the instance-property, but the enum itself is like a static variable.
So you cannot call an enum through an instance of the outer class in which it is declared. This is similar to the compiler error if you try to use a static field through an instance of it's class:
public class FooClass
{
public static string Foo = "Foo";
public string FooProp { get; set; }
}
You cannot access the static field FooClass.Foo via instance either:
var foo = new FooClass();
foo.FooProp = foo.Foo; // does not compile either, you have to use FooClass.Foo
An enum consists of a set of named constants, const is static implicitly.
Why does the compiler don't let me use an instance? Because it tries to prevent you from obvious careless mistakes. You don't need an instance so don't use it.
I found how to make it compile, I am asking for the reason why it is not compiling how I did it
HandleAction.Terminate is a value of an enum.
Its value is not linked with an instance of HandleResourceReferencesParams but its a type nested in the object HandleResourceReferencesParams.
It is the definition of an enum, not a member like a property or variable which would be instantiated.
The only specificity of a nested definition (enum, struct or class) is its specific privileges with its parent class.
Related
To create a generic base class I want to have the type of my fields stored in variables. Something like this (THIS is just not working):
public class MyClass
{
public MyClass()
{
myType = typeof(string);
}
public Type myType { get; set; }
public myType MyProperty { get; set; }
}
I handle lots of models and ObservableCollections of models from a database with more than 70 tables that I get via Entity Framework Core and I don't want to copy/paste (DRY!) the business logic for every table just because of the type of changes.
Is there another approach to what I came up with?
As you've seen, what you are trying to do is simply not possible. A variable of type Type cannot be used to declare another variable. (The same applies for fields, properties, etc). What you are looking for is called Generics, specifically generic classes. Here is what your code would look like with a generic class:
public class MyClass<T>
{
public T MyProperty { get; set; }
public Type TypeOfT => typeof(T); // for demo
}
In the above example, T (called the generic type parameter) is a sort of placeholder. It can be replaced with any* class/struct/interface. This is done when you create a new instance of the type:
var objInt = new MyClass<int>();
Console.WriteLine(objInt.TypeOfT.Name); // System.Int32
objInt.MyProperty = 5;
var objString = new MyClass<string>();
Console.WriteLine(objString.TypeOfT.Name); // System.String
objString.MyProperty = "Hello!";
They may even be used with other generic types:
var obj = new MyClass<MyClass<string>>();
obj.MyProperty = new MyClass<string>();
obj.MyProperty.MyProperty = "World";
Console.WriteLine(obj.TypeOfT.Name); // MyClass`1
Console.WriteLine(obj.MyProperty.TypeOfT.Name); // System.String
The type parameter remains consistent thoroughout the entire class. It can appear in properties, fields, return types and method parameters. A class can have multiple type parameters as well. Additionally, methods may specify generic arguments with or without their containing class being generic themselves.
I suggest following up with reading the docs pointed to by the above links. Generics is quite a big topic--one that cannot be done justice here.
* There are some types that cannot be used as generic arguments, for example ref structs
I have variable in my class with type of 'Type':
class A
{
public static Type MyType;
}
And I declared this variable with anywhere construction. Example:
A.MyType = b.GetType();
First problem: I need declare new variable with type stored in MyType.
<MyType> myValue = new <MyType>;
Second problem: I need a function that indicates whether reducible to each other variables of the 'Type'.
First problem: I need declare new variable with type stored in MyType.
You can't do this, basically.
Variable types need to be known at compile-time. You can't declare a variable using a type which is only known at execution time. You can get some of the way there using generics:
public class Foo<T>
{
private T value;
}
You can even add a constraint to require that T has a parameterless constructor which you can then call:
public class Foo<T> : new()
{
private T value = new T();
}
... and you could create a Foo<T> when you only know the type of T at execution time using reflection:
Type genericDefinition = typeof(Foo<>);
Type constructed = genericDefinition.MakeGenericType(A.MyType);
object instance = Activator.CreateInstance(constructed);
... but it will get messy pretty quickly. You haven't told us anything about what you're actually trying to achieve, so it's hard to suggest an alternative - but if you can possibly redesign to avoid this, I would.
Second problem: I need a function that indicates whether reducible to each other variables of the 'Type'
It's possible that you're looking for Type.IsAssignableFrom, but it's not clear...
public class EnumRouteConstraint<T> : IRouteConstraint
where T : struct
{
private static readonly Lazy<HashSet<string>> _enumNames; // <--
static EnumRouteConstraint()
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException(
Resources.Error.EnumRouteConstraint.FormatWith(typeof(T).FullName));
}
string[] names = Enum.GetNames(typeof(T));
_enumNames = new Lazy<HashSet<string>>(() => new HashSet<string>
(
names.Select(name => name), StringComparer.InvariantCultureIgnoreCase
));
}
public bool Match(HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
bool match = _enumNames.Value.Contains(values[parameterName].ToString());
return match;
}
}
Is this wrong? I would assume that this actually has a static readonly field for each of the possible EnumRouteConstraint<T> that I happen to instance.
It's fine to have a static field in a generic type, so long as you know that you'll really get one field per combination of type arguments. My guess is that R# is just warning you in case you weren't aware of that.
Here's an example of that:
using System;
public class Generic<T>
{
// Of course we wouldn't normally have public fields, but...
public static int Foo;
}
public class Test
{
public static void Main()
{
Generic<string>.Foo = 20;
Generic<object>.Foo = 10;
Console.WriteLine(Generic<string>.Foo); // 20
}
}
As you can see, Generic<string>.Foo is a different field from Generic<object>.Foo - they hold separate values.
From the JetBrains wiki:
In the vast majority of cases, having a static field in a generic type
is a sign of an error. The reason for this is that a static field in a
generic type will not be shared among instances of different close
constructed types. This means that for a generic class C<T> which
has a static field X, the values of C<int>.X and C<string>.X
have completely different, independent values.
In the rare cases when you do need the 'specialized' static fields,
feel free to suppress the warning.
If you need to have a static field shared between instances with
different generic arguments, define a non-generic base class to
store your static members, then set your generic type to inherit from
this type.
This is not necessarily an error - it is warning you about a potential misunderstanding of C# generics.
The easiest way to remember what generics do is the following:
Generics are "blueprints" for creating classes, much like classes are "blueprints" for creating objects. (Well, this is a simplification though. You may use method generics as well.)
From this point of view MyClassRecipe<T> is not a class -- it is a recipe, a blueprint, of what your class would look like. Once you substitute T with something concrete, say int, string, etc., you get a class. It is perfectly legal to have a static member (field, property, method) declared in your newly created class (as in any other class) and no sign of any error here.
It would be somewhat suspicious, at first sight, if you declare static MyStaticProperty<T> Property { get; set; } within your class blueprint, but this is legal too. Your property would be parameterized, or templated, as well.
No wonder in VB statics are called shared. In this case however, you should be aware that such "shared" members are only shared among instances of the same exact class, and not among the distinct classes produced by substituting <T> with something else.
There are several good answers here already, that explain the warning and the reason for it. Several of these state something like having a static field in a generic type generally a mistake.
I thought I'd add an example of how this feature can be useful, i.e. a case where suppressing the R#-warning makes sense.
Imagine you have a set of entity-classes that you want to serialize, say to Xml. You can create a serializer for this using new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)), but then you will have to create a separate serializer for each type. Using generics, you can replace that with the following, which you can place in a generic class that entities can derive from:
new XmlSerializerFactory().CreateSerializer(typeof(T))
Since your probably don't want to generate a new serializer each time you need to serialize an instance of a particular type, you might add this:
public class SerializableEntity<T>
{
// ReSharper disable once StaticMemberInGenericType
private static XmlSerializer _typeSpecificSerializer;
private static XmlSerializer TypeSpecificSerializer
{
get
{
// Only create an instance the first time. In practice,
// that will mean once for each variation of T that is used,
// as each will cause a new class to be created.
if ((_typeSpecificSerializer == null))
{
_typeSpecificSerializer =
new XmlSerializerFactory().CreateSerializer(typeof(T));
}
return _typeSpecificSerializer;
}
}
public virtual string Serialize()
{
// .... prepare for serializing...
// Access _typeSpecificSerializer via the property,
// and call the Serialize method, which depends on
// the specific type T of "this":
TypeSpecificSerializer.Serialize(xmlWriter, this);
}
}
If this class was NOT generic, then each instance of the class would use the same _typeSpecificSerializer.
Since it IS generic however, a set of instances with the same type for T will share a single instance of _typeSpecificSerializer (which will have been created for that specific type), while instances with a different type for T will use different instances of _typeSpecificSerializer.
An example
Provided the two classes that extend SerializableEntity<T>:
// Note that T is MyFirstEntity
public class MyFirstEntity : SerializableEntity<MyFirstEntity>
{
public string SomeValue { get; set; }
}
// Note that T is OtherEntity
public class OtherEntity : SerializableEntity<OtherEntity >
{
public int OtherValue { get; set; }
}
... let's use them:
var firstInst = new MyFirstEntity{ SomeValue = "Foo" };
var secondInst = new MyFirstEntity{ SomeValue = "Bar" };
var thirdInst = new OtherEntity { OtherValue = 123 };
var fourthInst = new OtherEntity { OtherValue = 456 };
var xmlData1 = firstInst.Serialize();
var xmlData2 = secondInst.Serialize();
var xmlData3 = thirdInst.Serialize();
var xmlData4 = fourthInst.Serialize();
In this case, under the hood, firstInst and secondInst will be instances of the same class (namely SerializableEntity<MyFirstEntity>), and as such, they will share an instance of _typeSpecificSerializer.
thirdInst and fourthInst are instances of a different class (SerializableEntity<OtherEntity>), and so will share an instance of _typeSpecificSerializer that is different from the other two.
This means you get different serializer-instances for each of your entity types, while still keeping them static within the context of each actual type (i.e., shared among instances that are of a specific type).
**Preface I'm very new to generics. I have a class I created called Geno. My Peno class simply contains a string Name property. I'm wondering why I cannot call T.Name from within a method on this class. How would I access the properties on T? Any good resources on how this works?
public class Geno<T> where T : Peno
{
}
public string GetName()
{
return T.Name;
}
Is your Name property an instance property or a static property? If it's a static property, you don't get polymorphism anyway - you could just call Peno.Name.
If it's an instance property, you need an instance on which to call it. For example:
public class Geno<T> where T : Peno
{
private readonly T value;
public Geno(T value)
{
this.value = value;
}
public string GetName()
{
return value.Name;
}
}
If this doesn't help, please give a more complete example of what you're trying to do.
T is the type Peno. So what you try to do is Peno.Name, which is probably not what you want. Something like this would work:
public string GetName(T t)
{
return t.Name;
}
Static properties does not support inheritence. Hence you can just call Peno.Name rather than going for T.
If in your case you want to access a normal property, you need to create an instance of T which will let you call its property Name.
T is a Generic Type Parameter, not a parameter being passed into the class that is using T.
A Good example of this is List<T> you say this is a List, which means you are creating a list that contains object of the type of String.
How do I explicitly refer to the parameter as opposed to the member variable?
static recursive{
public static List<string> output = new List<string>();
public static void Recursive(List<string> output){
...
}
}
An unqualified reference will always refer to the parameter because it is at a more local scope.
If you want to refer to the member variable, you need to qualify it with the name of the class (or this, for non-static member variables).
output = foo; // refers to the parameter
recursive.output = foo; // refers to a static member variable
this.output = foo; // refers to a non-static member variable
But you should probably change the name anyway. It makes your code much easier to read.
And you shouldn't have public static variables at all. All of the .NET coding style guidelines strongly recommend properties instead of exposing public fields. And since those are always camel-cased, this problem solves itself.
public static void Recursive(List<string> output){
...
}
The code in the block that refers to output will always be local & not the member variable.
If you wish to refer to member variable, you could use recursive.output.
When you are inside the Recursive static method output will point to the argument of the method. If you want to point to the static field use the name of the static class as prefix: recursive.output
Give your member variable another name.
The convention is to use Camelcasing on public static members.
public static List<string> Output = new List<string>();
public static void Recursive( List<string> output )
{
Output = output;
}
You can explicitly reference recursive.output to indicate the static member, but it would be cleaner to rename either the parameter or the member.
I know of no way to explicitly refer to a parameter. The way this is usually handled is to give member variables a special prefix such as _ or m_ so that parameters will never have exactly the same name. The other way is to refer to member variables using this.var.
public class MyClass {
public int number = 15;
public void DoSomething(int number) {
Console.WriteLine(this.number); // prints value of "MyClass.number"
Console.WriteLine(number); // prints value of "number" parameter
}
}
EDIT::
For static fields is required name of class instead of "this":
public class MyClass {
public static int number = 15;
public void DoSomething(int number) {
Console.WriteLine(this.number); // prints value of "MyClass.number"
Console.WriteLine(MyClass.number); // prints value of "number" parameter
}
}