Automatyczne wywołanie funkcji po inicjalizacji obiektu

Automatyczne wywołanie funkcji po inicjalizacji obiektu
KO
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 463
0

Czy w C# jest opcja na automatyczne wywołanie funkcji po inicjalizacji obiektu?
Chodzi mi o to, że w C# można zainicjować obiekt na 3 sposoby:

  1. bezpośrednio w klasie
  2. w konstruktorze
  3. w klamrach tworząc obiekt (?) - nie wiem jak to się nazywa

Przykładowo:

Kopiuj
class Test
	{
	public Test(int b = 2)
		{
		this.b = b;
		}

	public int a { get; init; } = 1;
	public int b { get; init; }
	public required int c { get; init; }
	}

Test test = new(2)
	{
	c = 3
	};

Chciałbym teraz korzystając z tego trzeciego sposobu wywołać jakąś funkcję inicjalizującą, która zostanie wywołana automatycznie po inicjalizacji wszystkich właściwości.
Jedyny sposób na jaki obecnie wpadłem jak to zrobić to coś w stylu:

Kopiuj
class Test
	{
	public Test()
		{}
	private void OnInit()
		{
		
		}
	private int _a;
	private int _b;
	private int _c;

	public required int a
		{
		get => _a; init
			{
			_a = value;
			OnInit();
			}
		}

	public required int b
		{
		get => _b; init
			{
			_b = value;
			OnInit();
			}
		}

	public required int c { get => _c; init
			{
			_c = value;
			OnInit();
			}
		}
	}

A może jest jakiś lepszy sposób, który wywoła tylko raz funkcję OnInit?

obscurity
  • Rejestracja: dni
  • Ostatnio: dni
1
Kofcio napisał(a):
  1. w klamrach tworząc obiekt (?) - nie wiem jak to się nazywa

to się nazywa object initializer.

Niestety nie ma, jedynie niektóre serializery mogą wywoływać metodę po deserializacji, ale do typowego ręcznego tworzenia obiektu nie ma czegoś takiego.
Możesz użyć factory method żeby utworzyć i zainicjalizować obiekt w jednej instrukcji, albo moim zdaniem lepiej - zainicjalizować obiekt explicit lub ewentualnie przed samym jego użyciem w jednym miejscu np:

Kopiuj
class Test
{
	public Test(int b = 2)
		{
		this.b = b;
		}

	public int a { get; init; } = 1;
	public int b { get; init; }
	public required int c { get; init; }

    private bool initialized;

    private void EnsureIsInitialized()
    {
      if (initialized) return;
      initialized = true;
      // ...
    }

    public void DoSth() {
      EnsureIsInitialized();
      // ...
    }
}

tu oczywiście dochodzi problem wielowątkowości, dlatego ogólnie odradzam takie podejście, ale musiałbyś pokazać więcej kodu.

Możesz też na przykład mieć osobny rekord do trzymania propertiesów a osobny do korzystania z nich i inicjować się w konstruktorze:

Kopiuj
class TestOptions { // record?
	public int a { get; init; } = 1;
	public int b { get; init; }
	public required int c { get; init; }
}

class Test {
    public Test(TestOptions options) {
      // init używając options
    }
}

Wydaje mi się że to ostatnie podejście jest najszerzej stosowane i jest czytelne i najbardziej logiczne.

btw od c# 14 nie musisz pisać

Kopiuj
	private int _a;

	public required int a
		{
		get => _a; init
			{
			_a = value;
			OnInit();
			}
		}

możesz użyć keyword field i napisać to tak:

Kopiuj
	public required int a
    {
        get;
        init
        {
          field = value;
          OnInit();
        }
    }
WeiXiao
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 5226
1
Kopiuj
public class Test
{
    public int A { get; set; }
    public int B { get; set; }
    private bool Initialized { get; set; }

    private Test(int a, int b)
    {
        A = a;
        B = b;
    }

    private void Initialize()
    {
        Initialized = true;
    }

    public static Test CreateTest(int a, int b)
    {
        var test = new Test(a, b);
        test.Initialize();
        return test;
    }
}


static void Main()
{
    var test = Test.CreateTest(3, 4); // ok

    var test2 = new Test(1, 2); // compilation error;
}

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.